npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@newschools/sdk

v0.2.8

Published

New Schools SDK - Multi-framework components and modules for integrating New Schools learning content

Readme

@newschools/sdk

Nuxt module for integrating New Schools learning content into your applications with automatic organization theming.

Features

Auto-imported composables - useAsyncNewSchools and useOrganization available everywhere
🎨 Automatic theming - Organization brand colors applied as CSS variables
🔒 Type-safe - Full TypeScript support with entity types
SSR-compatible - Built on Nuxt's useAsyncData
🎯 Ready-to-use components - EntityHero, EntityBentoGrid, EntityCard
📐 Smart layouts - EntityBentoGrid automatically generates responsive layouts
🔑 API key authentication - Secure access to your organization's content

Installation

# Using npm
npm install @newschools/sdk

# Using yarn
yarn add @newschools/sdk

# Using pnpm
pnpm add @newschools/sdk

# Using bun
bun add @newschools/sdk

Setup

1. Add module to nuxt.config.ts

export default defineNuxtConfig({
  modules: ["@newschools/sdk"],

  newschools: {
    apiKey: process.env.NUXT_PUBLIC_NEWSCHOOLS_API_KEY,
    organizationSlug: "my-organization", // Optional: auto-fetch org data and apply brand colors
  },
});

2. Configure API key

Add your New Schools API key to .env:

NUXT_PUBLIC_NEWSCHOOLS_API_KEY=ns_live_xxxxxxxxxxxxx

3. (Optional) Configure organization

If you want to automatically load organization data and apply brand theming:

export default defineNuxtConfig({
  modules: ["@newschools/sdk"],

  newschools: {
    apiKey: process.env.NUXT_PUBLIC_NEWSCHOOLS_API_KEY,
    organizationSlug: "webdesignwill", // Loads org data on init
  },
});

When organizationSlug is set, the SDK will:

  • ✅ Auto-fetch organization data on app initialization
  • ✅ Apply brand colors (primary, secondary, tertiary) as CSS variables
  • ✅ Make organization data available via useOrganization() composable

Automatic Brand Theming

The SDK automatically applies your organization's brand colors as CSS variables:

// In nuxt.config.ts
export default defineNuxtConfig({
  newschools: {
    organizationSlug: "my-org", // Has primary: "#FF0000"
  },
});

The SDK sets:

  • --ns-color-primary#FF0000
  • --ns-color-secondary → (your secondary color)
  • --ns-color-tertiary → (your tertiary color)

If colors are not set or cleared by admin, they revert to SDK defaults:

  • --ns-color-primary#4a3f8f (Deep Purple)
  • --ns-color-secondary#ff6b6b (Vibrant Coral)
  • --ns-color-tertiary#0fa3b1 (Rich Teal)

Use these variables in your components:

<template>
  <div class="my-component">
    <h1>Automatically themed!</h1>
  </div>
</template>

<style scoped>
.my-component {
  background: var(--ns-color-primary);
  color: white;
}

h1 {
  border-bottom: 2px solid var(--ns-color-secondary);
}
</style>

Usage

Organization Data

Access organization data loaded by the SDK:

<script setup lang="ts">
const organization = useOrganization();
</script>

<template>
  <div v-if="organization">
    <h1>{{ organization.name }}</h1>
    <img v-if="organization.coverImageUrl" :src="organization.coverImageUrl" />
  </div>
</template>

Pre-built Components

EntityHero

Display entity header with cover image and branding. Supports 4 layout variants:

Basic Usage:

<template>
  <EntityHero :entity="organization" variant="left">
    <template #sub-title>
      <p>Welcome to our learning platform</p>
    </template>

    <template #content>
      <div>Custom content area for CTAs, forms, etc.</div>
    </template>
  </EntityHero>
</template>

<script setup lang="ts">
const organization = useOrganization();
</script>

Layout Variants:

  • variant="left" (default) - Image on left, content on right (grid on tablet+, stacked on mobile)
  • variant="right" - Content on left, image on right (grid on tablet+, stacked on mobile)
  • variant="fullscreen" - Full screen image with content overlay at bottom left (all screen sizes)
  • variant="horizontal" - Image on top (50vh with clamp), content below (stacked on all sizes)

Variant Examples:

<!-- Fullscreen: Content overlays image -->
<EntityHero :entity="organization" variant="fullscreen">
  <template #sub-title>Journey subtitle</template>
  <template #content>
    <p>This content appears over the image, under the title.</p>
  </template>
</EntityHero>

<!-- Horizontal: Image top, content bottom -->
<EntityHero :entity="organization" variant="horizontal">
  <template #sub-title>Course overview</template>
  <template #content>
    <div>Course details and enrollment form</div>
  </template>
</EntityHero>

<!-- Right: Reversed grid layout -->
<EntityHero :entity="organization" variant="right">
  <template #sub-title>Featured content</template>
  <template #content>
    <button>Get Started</button>
  </template>
</EntityHero>

Responsive Behavior:

  • Title and subtitle always overlay the image (all variants)
  • left and right variants stack vertically on mobile (<768px)
  • fullscreen maintains overlay layout on all screen sizes
  • horizontal uses flexible image height: clamp(40vh, 50vh, 60vh)

EntityBentoGrid (Recommended)

Display entities in a responsive bento grid with automatic layout:

<template>
  <!-- Simple: Auto-renders journeys as cards -->
  <EntityBentoGrid :items="journeys" />

  <!-- Advanced: Custom card rendering -->
  <EntityBentoGrid :items="journeys">
    <template #item="{ item, index }">
      <EntityCard :image-url="item.coverImageUrl" :image-alt="item.title">
        <template #title>
          <h3>{{ item.title }}</h3>
        </template>
        <template #description>
          <p>{{ item.description }}</p>
        </template>
      </EntityCard>
    </template>
  </EntityBentoGrid>
</template>

<script setup lang="ts">
const { data: journeys } = await useAsyncNewSchools("journeys");
</script>

What it does:

  • ✅ Automatically generates optimized layouts for 1-6+ items
  • ✅ Works with any entity type (journeys, teachers, waypoints, activities)
  • ✅ Default rendering extracts title, description, and images automatically
  • ✅ Fully customizable via #item slot
  • ✅ Responsive (mobile-first, 3-column grid on tablet+)

BentoGrid (Advanced)

For complete manual control, use BentoGrid directly:

Create responsive masonry-style layouts:

<template>
  <BentoGrid>
    <!-- 2x2 Featured card -->
    <BentoGridItem :col-span="2" :row-span="2">
      <EntityCard image-url="/hero.jpg" image-alt="Featured content">
        <template #title>
          <h3>Featured Content</h3>
        </template>
        <template #description>
          <p>Main featured content with prominent display</p>
        </template>
      </EntityCard>
    </BentoGridItem>

    <!-- Standard cards -->
    <BentoGridItem>
      <EntityCard image-url="/card.jpg">
        <template #title>
          <h3>Regular Card</h3>
        </template>
        <template #description>
          <p>Standard card content</p>
        </template>
      </EntityCard>
    </BentoGridItem>

    <!-- Explicit positioning (advanced) -->
    <BentoGridItem :col-span="2" :row-span="2" :col-start="2" :row-start="3">
      <EntityCard image-url="/bottom-right.jpg">
        <template #title>
          <h3>Bottom Right</h3>
        </template>
        <template #description>
          <p>Explicitly positioned at column 2, row 3</p>
        </template>
      </EntityCard>
    </BentoGridItem>
  </BentoGrid>
</template>

Fetching Learning Content

The useAsyncNewSchools composable is auto-imported and available everywhere.

Display journeys in a bento grid

<script setup lang="ts">
const { data: journeys } = await useAsyncNewSchools("journeys");
</script>

<template>
  <EntityBentoGrid v-if="journeys" :items="journeys" />
</template>

Fetch all journeys

<script setup lang="ts">
const { data: journeys, error } = await useAsyncNewSchools("journeys");
</script>

<template>
  <div v-if="journeys">
    <div v-for="journey in journeys" :key="journey.id">
      <h2>{{ journey.title }}</h2>
      <p>{{ journey.description }}</p>
    </div>
  </div>
</template>

Fetch a specific journey

<script setup lang="ts">
const route = useRoute();
const { data: journey } = await useAsyncNewSchools("journey", {
  id: route.params.slug,
});
</script>

<template>
  <div v-if="journey">
    <h1>{{ journey.title }}</h1>
    <p>{{ journey.description }}</p>
  </div>
</template>

Fetch waypoints for a journey

<script setup lang="ts">
const { data: waypoints } = await useAsyncNewSchools("waypoints", {
  journeyId: "my-journey-slug",
});
</script>

Fetch a specific waypoint

<script setup lang="ts">
const { data: waypoint } = await useAsyncNewSchools("waypoint", {
  journeyId: "journey-slug",
  waypointId: "waypoint-slug",
});
</script>

TypeScript Support

Full type safety for all entities:

import type {
  Organisation,
  OrganisationBrand,
  Journey,
  Waypoint,
  Activity,
  JourneyTheme,
  WaypointType,
  LayoutMode,
} from "@newschools/sdk";

// Types are automatically inferred in composables
const organization = useOrganization();
// organization is typed as Ref<Organisation | null>

const { data: journeys } = await useAsyncNewSchools("journeys");
// journeys is typed as Journey[] | null

Module Configuration

All configuration options for nuxt.config.ts:

export default defineNuxtConfig({
  newschools: {
    /**
     * New Schools API key (required)
     * Can also be set via NUXT_PUBLIC_NEWSCHOOLS_API_KEY env variable
     */
    apiKey: string;

    /**
     * Base URL for New Schools API
     * Default: "https://newschools.ai"
     */
    baseUrl?: string;

    /**
     * Organization slug for auto-fetch and theming
     * When set, SDK will:
     * - Automatically fetch organization data on init
     * - Apply brand colors as CSS variables
     * - Make data available via useOrganization()
     *
     * Example: "my-organization"
     */
    organizationSlug?: string;
  },
});

Do you need baseUrl?

No, unless you're using a custom API endpoint or testing against a development server.

The default value is "https://newschools.ai", which points to production.

// ✅ Minimal config (most common)
export default defineNuxtConfig({
  newschools: {
    apiKey: process.env.NUXT_PUBLIC_NEWSCHOOLS_API_KEY,
  },
});

// ✅ With organization theming
export default defineNuxtConfig({
  newschools: {
    apiKey: process.env.NUXT_PUBLIC_NEWSCHOOLS_API_KEY,
    organizationSlug: "webdesignwill",
  },
});

// ⚠️ Only needed for custom environments
export default defineNuxtConfig({
  newschools: {
    apiKey: process.env.NUXT_PUBLIC_NEWSCHOOLS_API_KEY,
    baseUrl: "https://staging.newschools.ai", // Testing against staging
  },
});

Available CSS Variables

The SDK provides themeable CSS tokens:

Brand Colors (Automatically set from organization)

  • --ns-color-primary - Primary brand color
  • --ns-color-secondary - Secondary brand color
  • --ns-color-tertiary - Tertiary brand color

Additional Tokens

  • --ns-color-grey-800, --ns-color-grey-900
  • --ns-spacing-xs through --ns-spacing-3xl
  • --ns-border-radius-sm through --ns-border-radius-2xl
  • --ns-shadow-sm through --ns-shadow-xl

See tokens.css for the complete list.

Advanced Usage

Manual Client (Without Nuxt Composables)

For use in server routes, plugins, or non-component contexts:

import { createClient } from "@newschools/sdk";

const client = createClient({
  apiKey: "ns_live_xxxxxxxxxxxxx",
  baseUrl: "https://newschools.ai", // optional
});

const journeys = await client.get<Journey[]>("/journeys");
const journey = await client.get<Journey>("/journeys/my-slug");

Environment Variables

| Variable | Description | Required | Default | | --------------------------------- | ------------------------ | -------- | --------------------- | | NUXT_PUBLIC_NEWSCHOOLS_API_KEY | Your New Schools API key | Yes | - | | NUXT_PUBLIC_NEWSCHOOLS_BASE_URL | API base URL | No | https://newschools.ai |

Components Reference

<EntityHero>

Display entity header with cover image and branding

Props:

  • entity: Organisation - Entity object (Organization, Journey, etc.) (required)
  • variant?: 'left' | 'right' | 'fullscreen' | 'horizontal' - Layout variant (default: 'left')

Slots:

  • sub-title - Content below entity name (overlays image)
  • content - Main content area (position varies by variant)

Layout Behavior:

  • left: Grid layout (image left, content right) on tablet+, stacks on mobile
  • right: Grid layout (content left, image right) on tablet+, stacks on mobile
  • fullscreen: Content overlays image at bottom left (all screen sizes)
  • horizontal: Image top with flexible height, content below (all screen sizes)

<EntityBentoGrid>

Automatically display entities in a responsive bento grid layout

Props:

  • items: any[] - Array of entities (journeys, teachers, waypoints, etc.) (required)
  • key-field?: string - Custom key field for v-for (default: 'id')

Slots:

  • item - Scoped slot for custom item rendering
    • item - The entity object
    • index - Item index

Auto-extracts fields:

  • Title: title or name
  • Description: description or summary
  • Image: coverImageUrl, avatarUrl, or imageUrl

Layout patterns:

  • 1 item: Full width
  • 2 items: 2/3 + 1/3 split
  • 3 items: 1 large (2x2) + 2 small stacked
  • 4 items: 1 large (2x2) + 2 small + 1 wide bottom
  • 5 items: 1 large + 4 small corners
  • 6+ items: Mirrored pattern (repeating)

<BentoGrid>

Responsive masonry-style grid layout (advanced manual control)

<BentoGridItem>

Grid item with flexible sizing and optional explicit positioning

Props:

  • col-span?: number - Column span (1-3, default: 1)
  • row-span?: number - Row span (1-2, default: 1)
  • col-start?: number - Column start position (1-3, optional for explicit positioning)
  • row-start?: number - Row start position (1+, optional for explicit positioning)

Note: When col-start and row-start are omitted, items use CSS Grid auto-placement. Use explicit positioning for complex layouts where auto-flow doesn't achieve the desired result.

<EntityCard>

Card component for displaying content with automatic text clamping

Props:

  • image-url?: string - Cover image URL
  • image-alt?: string - Image alt text

Slots:

  • title - Card title (automatically clamped to 2 lines with ellipsis)
  • description - Card description (automatically clamped to 3 lines with ellipsis)

Example:

<EntityCard
  image-url="https://example.com/image.jpg"
  image-alt="Course thumbnail"
>
  <template #title>
    <h3>{{ journey.title }}</h3>
  </template>
  <template #description>
    <p>{{ journey.description }}</p>
  </template>
</EntityCard>

<EntityImage>

Optimized image component with loading states

Props:

  • src?: string - Image URL
  • alt: string - Alt text

Composables Reference

useOrganization()

Access globally loaded organization data

Returns: Ref<Organisation | null>

useAsyncNewSchools(resource, params?)

Fetch data from New Schools v1 API with SSR support

Resources:

  • 'journeys' - List all journeys
  • 'journey' - Get single journey
  • 'waypoints' - List waypoints
  • 'waypoint' - Get single waypoint
  • 'activities' - List activities
  • 'activity' - Get single activity

Returns: AsyncData<T>

License

MIT