Crux
Getting Started

Next.js

Wire Crux into a Next.js 16 App Router project — Server Actions, streaming Route Handlers, and the recommended file layout.

This guide gets Crux running in a Next.js 16 App Router project. Crux prompts run on the server — define them in a shared module and call them from Server Actions or Route Handlers.

Crux is in alpha. The install commands below are the intended package set for the first public npm alpha; until that release lands, consume Crux from this repository workspace.

Install

bash npm install @crux/core @crux/ai @ai-sdk/openai zod
bash pnpm add @crux/core @crux/ai @ai-sdk/openai zod
bash yarn add @crux/core @crux/ai @ai-sdk/openai zod
bash bun add @crux/core @crux/ai @ai-sdk/openai zod
.env.local
OPENAI_API_KEY=sk-...

The @ai-sdk/openai provider reads OPENAI_API_KEY automatically.

app/
  actions/
    summarize.ts        ← Server Action that calls generate()
  api/
    chat/route.ts       ← Route Handler with streaming
lib/
  ai/
    prompts.ts          ← Prompt definitions
    contexts.ts         ← Shared contexts
    config.ts           ← policy/defaults config (optional)

This is a recommendation, not a requirement. Crux has no required project structure — organize however fits your codebase.

Step 1: Define a prompt

Create the prompt module

lib/ai/prompts.ts
import { prompt, context } from '@crux/core'
import { z } from 'zod'

const brand = context({
  id: 'brand',
  priority: 30,
  system: 'Use a professional tone. Be concise.',
})

export const summarize = prompt({
  id: 'summarize',
  use: [brand],
  input: z.object({ text: z.string() }),
  output: z.object({
    summary: z.string(),
    keyPoints: z.array(z.string()),
  }),
  system: 'Summarize the given text.',
  prompt: ({ input }) => input.text,
})

Call it from a Server Action

app/actions/summarize.ts
'use server'

import { generate } from '@crux/ai'
import { openai } from '@ai-sdk/openai'
import { summarize } from '@/lib/ai/prompts'

export async function summarizeText(text: string) {
  const result = await generate(summarize, {
    model: openai('gpt-4o-mini'),
    input: { text },
  })

  return result.object
}

Use it in a Server Component or Client Component

app/page.tsx
import { summarizeText } from './actions/summarize'

export default async function Page() {
  const result = await summarizeText('Long article text here...')

  return (
    <article>
      <p>{result.summary}</p>
      <ul>
        {result.keyPoints.map((point) => (
          <li key={point}>{point}</li>
        ))}
      </ul>
    </article>
  )
}

Streaming from a Route Handler

For chat or any long-form output, stream from a Route Handler:

app/api/chat/route.ts
import { stream } from '@crux/ai'
import { openai } from '@ai-sdk/openai'

import { chatPrompt } from '@/lib/ai/prompts'

export async function POST(req: Request) {
  const { message } = await req.json()

  const result = await stream(chatPrompt, {
    model: openai('gpt-4o'),
    input: { message },
  })

  return result.toUIMessageStreamResponse()
}

The Vercel AI SDK adapter's stream() returns a result compatible with toUIMessageStreamResponse() for seamless integration with useChat on the client.

Next steps

On this page