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

@dbosoft/nextjs-tutorials

v0.2.3

Published

Interactive tutorial/learning system for dbosoft sites

Downloads

51

Readme

@dbosoft/nextjs-tutorials

Interactive tutorial system for Next.js sites. Server/client component architecture with data-driven blocks, variant paths, and progress tracking.

Installation

{
  "dependencies": {
    "@dbosoft/nextjs-tutorials": "workspace:*",
    "@dbosoft/nextjs-site-core": "workspace:*",
    "@dbosoft/react-uicore": "workspace:*"
  },
  "peerDependencies": {
    "react": ">=18",
    "next": ">=16",
    "@headlessui/react": ">=2",
    "@heroicons/react": ">=2",
    "next-intl": ">=4",
    "clsx": ">=2"
  }
}

Quick Start

1. Define a tutorial

// lib/tutorials-server.ts
import { createTutorialServer } from '@dbosoft/nextjs-tutorials/server'
import type { TutorialDefinition } from '@dbosoft/nextjs-tutorials/types'

export type MyVariant = 'cli' | 'gui'

const tutorial: TutorialDefinition<MyVariant> = {
  slug: 'getting-started',
  title: 'Getting Started',
  description: 'Learn the basics.',
  difficulty: 'beginner',
  estimatedTime: '15 min',
  variants: [
    { id: 'cli', label: 'CLI' },
    { id: 'gui', label: 'GUI' },
  ],
  defaultVariant: 'cli',
  prerequisites: ['Node.js 18 or later'],
  learningObjectives: ['Set up the development environment'],
  lessons: [
    {
      id: 'setup',
      title: 'Setup',
      description: 'Install and configure.',
      steps: [
        { id: 'install', title: 'Installation', estimatedTime: 3 },
        { id: 'config', title: 'Configuration', estimatedTime: 5 },
      ],
    },
  ],
}

export const tutorialServer = createTutorialServer<MyVariant>({
  registry: [tutorial],
  stepLoader: async (slug, lesson, step) => {
    const mod = await import(`@/tutorials/${slug}/${lesson}/${step}`)
    return mod.default
  },
  pathPrefix: '/tutorials',
})

2. Create the pages

// app/tutorials/page.tsx
import { TutorialListPage } from '@dbosoft/nextjs-tutorials/layout'
import { tutorialServer } from '@/lib/tutorials-server'

export default async function TutorialsPage() {
  const tutorials = await tutorialServer.getAllTutorials()
  return <TutorialListPage tutorials={tutorials} title="Tutorials" />
}
// app/tutorials/[slug]/page.tsx
import { TutorialPage } from '@dbosoft/nextjs-tutorials/layout'
import {
  TutorialPathProvider,
  TutorialProgress,
  ProgressText,
} from '@dbosoft/nextjs-tutorials/components'
import { tutorialServer } from '@/lib/tutorials-server'
import type { MyVariant } from '@/lib/tutorials-server'

export default async function TutorialDetailPage({
  params,
  searchParams,
}: {
  params: Promise<{ slug: string }>
  searchParams: Promise<{ lesson?: string; step?: string; path?: string }>
}) {
  const { slug } = await params
  const { lesson, step, path } = await searchParams
  const tutorial = await tutorialServer.getTutorialBySlug(slug)
  if (!tutorial) notFound()

  const totalSteps = tutorial.lessons.reduce((sum, l) => sum + l.steps.length, 0)

  return (
    <TutorialPathProvider<MyVariant>
      tutorialSlug={tutorial.slug}
      variants={tutorial.variants ?? []}
      defaultVariant={tutorial.defaultVariant ?? 'cli'}
      initialVariant={path as MyVariant | undefined}
    >
      <TutorialProgress tutorialSlug={tutorial.slug} totalSteps={totalSteps}>
        <TutorialPage
          tutorial={tutorial}
          currentLesson={lesson}
          currentStep={step}
          stepLoader={tutorialServer.loadStep}
          pathPrefix="/tutorials"
        />
      </TutorialProgress>
    </TutorialPathProvider>
  )
}

3. Write step content

tutorials/
  getting-started/
    setup/
      install.tsx
      config.tsx

Each step is a React component that composes blocks:

// tutorials/getting-started/setup/install.tsx
import {
  TutorialSection,
  TutorialText,
  CodeExample,
  ConceptBox,
  TipBox,
  InlineCode,
  Emphasis,
} from '@dbosoft/nextjs-tutorials/blocks'
import { PathBranch } from '@dbosoft/nextjs-tutorials/components'
import type { MyVariant } from '@/lib/tutorials-server'

export default function Install() {
  return (
    <TutorialSection>
      <TutorialText>
        Install <InlineCode>@example/toolkit</InlineCode> using your
        preferred package manager.
      </TutorialText>

      <PathBranch<MyVariant>>
        {{
          cli: (
            <CodeExample
              title="Terminal"
              language="bash"
              code="npm install @example/toolkit"
              explanation="This installs the toolkit and all dependencies."
            />
          ),
          gui: (
            <TutorialText>
              Open the package manager UI and search for
              <Emphasis> @example/toolkit</Emphasis>, then click Install.
            </TutorialText>
          ),
        }}
      </PathBranch>

      <ConceptBox title="What gets installed?">
        <TutorialText>
          The core library, CLI tools, and default templates.
        </TutorialText>
      </ConceptBox>

      <TipBox title="Tip">
        <TutorialText>
          If you encounter permission errors, try elevated privileges.
        </TutorialText>
      </TipBox>
    </TutorialSection>
  )
}

Block Components

Blocks are the building units for tutorial content. They enforce consistent design across all tutorials.

Design Rules

  1. Blocks are data-driven. Pass strings, arrays, and structured objects as props. Never write raw HTML (<li>, <strong>, <code>, <p>) inside step content.

  2. children is for nesting blocks. Container blocks (TutorialSection, ConceptBox, TipBox, Exercise, PrerequisiteHint) accept children — but children must be other blocks, not raw content.

  3. Inline blocks go inside TutorialText. Use InlineCode, Emphasis, ExternalLink, and StepIndicator inside TutorialText for inline formatting.

// CORRECT: data-driven blocks, nested composition
<TutorialSection>
  <TutorialText>
    Run <InlineCode>node --version</InlineCode> to verify.
  </TutorialText>
  <CodeExample language="bash" code="node --version" />
  <TipBox title="Tip">
    <TutorialText>Use Node.js 18 or later.</TutorialText>
  </TipBox>
</TutorialSection>

// WRONG: raw HTML, content not wrapped in blocks
<TutorialSection>
  <p>Run <code>node --version</code> to verify.</p>
  <TipBox title="Tip">
    Use Node.js 18 or later.
  </TipBox>
</TutorialSection>

Block Reference

Core (inline text)

| Block | Props | Purpose | |-------|-------|---------| | TutorialSection | children | Top-level container with space-y-4 | | TutorialText | children | Body text paragraph (hosts inline blocks) | | TutorialHeading | level: 3\|4\|5, children | Section heading | | InlineCode | children, variant?: 'default'\|'accent' | Inline code span | | Emphasis | children | Bold text | | ExternalLink | href, children | External link with icon |

Content (data-driven)

| Block | Props | Purpose | |-------|-------|---------| | CodeExample | code, language?, title?, explanation? | Syntax-highlighted code block | | TerminalOutput | output, title?, variant? | Command output display | | TutorialList | items: (string\|ReactNode)[], ordered? | Bullet or numbered list | | ComparisonTable | headers: [string, string], rows: [string, string][] | Two-column comparison | | DefinitionList | items: {term, definition}[] | Term/definition pairs | | FeatureGrid | items: {icon?, title, description}[], columns?: 2\|3 | Feature cards grid | | FlowDiagram | steps: {icon?, label}[] | Horizontal step flow with arrows | | WorkflowSteps | steps: {icon?, title, description}[] | Numbered vertical steps | | BadgeWithDescription | items: {badge, description, variant?}[] | Badge + description list | | TutorialImage | src, alt, width?, height?, caption?, inline? | Image with optional lightbox |

Containers (accept nested blocks)

| Block | Props | Purpose | |-------|-------|---------| | ConceptBox | title, children | Key concept callout with left accent | | TipBox | type?: 'tip'\|'warning'\|'info'\|'success', title?, children | Tip/warning/info callout | | Exercise | title, children | Interactive exercise prompt | | PrerequisiteHint | children | Prerequisites warning callout |

Navigation

| Block | Props | Purpose | |-------|-------|---------| | NavigationHint | items: string[] | Breadcrumb-style path hint | | StepIndicator | current, total | Progress badge (e.g. "3/5") |

Variant Paths

Tutorials support multiple instruction paths (e.g. CLI vs GUI). The variant is stored in the URL (?path=cli) for SSR agreement, with localStorage fallback.

PathBranch

Wraps variant-specific content. Renders the PathSwitcher ("Instructions for: CLI | GUI") automatically at the fork point.

import { PathBranch } from '@dbosoft/nextjs-tutorials/components'

<PathBranch<MyVariant>>
  {{
    cli: <CodeExample code="npm start" language="bash" />,
    gui: <TutorialText>Click the Run button.</TutorialText>,
  }}
</PathBranch>

Use hideSwitcher on subsequent PathBranch blocks in the same step:

<PathBranch<MyVariant>>{{ /* first fork — shows switcher */ }}</PathBranch>
<PathBranch<MyVariant> hideSwitcher>{{ /* second fork — no switcher */ }}</PathBranch>

PathContent

Shows content only for a specific variant. Does not render the switcher — use inside a PathBranch or when the switcher isn't needed.

import { PathContent } from '@dbosoft/nextjs-tutorials/components'

<PathContent<MyVariant> variant="cli">
  <DefinitionList items={[...]} />
</PathContent>

Architecture

Server/Client Split

  • TutorialPage (server component): Resolves active lesson/step from URL, pre-loads all step components for the active lesson, renders the full page markup.
  • StepDisclosure (client component): Receives pre-loaded step content, manages toggle/collapse state client-side. No page reload for within-lesson navigation.
  • Lesson switching triggers a server round-trip (new lesson = new step components to load).

Progress Tracking

TutorialProgress uses useSyncExternalStore with localStorage. SSR renders with empty progress, client hydrates from storage without cascading re-renders.

URL Parameters

| Param | Purpose | |-------|---------| | lesson | Active lesson ID | | step | Active step ID (updated via replaceState) | | path | Active variant for SSR agreement |

Exports

| Path | Content | |------|---------| | @dbosoft/nextjs-tutorials/types | TypeScript types | | @dbosoft/nextjs-tutorials/server | createTutorialServer() | | @dbosoft/nextjs-tutorials/layout | TutorialPage, TutorialListPage | | @dbosoft/nextjs-tutorials/components | Client components (providers, disclosure, path, progress) | | @dbosoft/nextjs-tutorials/blocks | Content blocks | | @dbosoft/nextjs-tutorials/seo | TutorialSchema structured data | | @dbosoft/nextjs-tutorials/i18n | loadMessages() |