@newschools/sdk

New Schools SDK - Multi-framework components and modules for integrating New Schools learning content
2,312
Install
npm install @newschools/sdk
Latest Version:0.2.8
Vue:3
Last Updated:Feb 2, 2026
Links: npm
Maintainer: digitalknight

@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>
    
  </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)

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

Related Packages

@qelos/integrator-nuxt

Nuxt integrator for Qelos: server middleware/module that identifies the user and...

2,274
@x-app/nuxt-api

Configurable API composable for Nuxt 3 and Nuxt 4

393
@b10cks/nuxt

b10cks Nuxt client SDK

6,778 2
nuxt-jsonapi

Easy JSON:API client integration for Nuxt.js

5,760 44
@alikhalilll/nuxt-api-provider

Strongly-typed fetch client for Nuxt 3/4 with interceptors, retry/backoff, and a...

2,510 4

Version History

Version Released Vue License
0.2.8 ^3.0
0.2.7 ^3.0
0.2.6 ^3.0
0.2.5 ^3.0
0.2.4 ^3.0