Crux
Getting Started

Next.js

Wire Crux into a Next.js 16 App Router project, covering 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. Public packages are available on npm; use the install commands below to get started.

Install

bash npm install @use-crux/core @use-crux/ai @ai-sdk/openai zod

bash pnpm add @use-crux/core @use-crux/ai @ai-sdk/openai zod

bash yarn add @use-crux/core @use-crux/ai @ai-sdk/openai zod

bash bun add @use-crux/core @use-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 "@use-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 "@use-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 { createUIMessageStreamResponse, stream } from "@use-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 createUIMessageStreamResponse(result);
}

The Vercel AI SDK adapter's stream() returns a canonical Crux stream result. Pass it to createUIMessageStreamResponse(result) for seamless integration with useChat on the client.

Next steps

On this page