A snippet is a TypeScript function you put in the Code Mode prompt. The model calls that function instead of writing the same program again.
Snippets let the model save working code under a name. On later requests, the saved snippets load from storage, and the model calls them as tools.
Note
These snippets are runtime code that the chat model saves. To teach your coding assistant (Claude Code, Cursor) how TanStack AI works, see Agent Skills (TanStack Intent).
You need the route from the Code Mode quick setup first.
npm i @tanstack/ai-code-mode-snippetspnpm add @tanstack/ai-code-mode-snippetsyarn add @tanstack/ai-code-mode-snippetsbun add @tanstack/ai-code-mode-snippets// app/api/chat/route.ts (Next.js). Any route that takes a Request works.
import {
chat,
chatParamsFromRequest,
convertMessagesToModelMessages,
maxIterations,
toServerSentEventsResponse,
toolDefinition,
} from '@tanstack/ai'
import { codeModeWithSnippets } from '@tanstack/ai-code-mode-snippets'
import { createFileSnippetStorage } from '@tanstack/ai-code-mode-snippets/storage'
import { createNodeIsolateDriver } from '@tanstack/ai-isolate-node'
import { openaiText } from '@tanstack/ai-openai'
import { z } from 'zod'
const fetchWeather = toolDefinition({
name: 'fetchWeather',
description: 'Get current weather for a city',
inputSchema: z.object({ location: z.string() }),
outputSchema: z.object({ temperature: z.number(), condition: z.string() }),
}).server(async ({ location }) => ({
temperature: location.length + 10,
condition: 'sunny',
}))
const storage = createFileSnippetStorage({ directory: './.snippets' })
const driver = createNodeIsolateDriver()
export async function POST(request: Request) {
const { messages, threadId, runId } = await chatParamsFromRequest(request)
const { toolsRegistry, systemPrompt } = await codeModeWithSnippets({
config: { driver, tools: [fetchWeather] },
adapter: openaiText('gpt-5.4-mini'), // cheap model that picks snippets
snippets: { storage, maxSnippetsInContext: 5 },
messages: convertMessagesToModelMessages(messages),
threadId,
})
const stream = chat({
adapter: openaiText('gpt-6.1-sol'), // strong model for the real work
systemPrompts: ['You are a helpful weather assistant.', systemPrompt],
tools: toolsRegistry.getTools(),
messages,
threadId,
runId,
agentLoopStrategy: maxIterations(15),
})
return toServerSentEventsResponse(stream)
}Send "Compare the weather in Tokyo and Paris. Save the code as a snippet."
Look in ./.snippets/. It holds a new folder with code.ts and meta.json.
Start a new chat and ask the same kind of question. The cheap model picks the saved snippet, and the main model calls it as a tool instead of writing the code again.
Each request goes through these steps:
If storage is empty or there are no messages, step 2 makes no model call.
The registry holds these tools:
A new snippet is callable inside execute_typescript right away, as snippet_<name>. It shows up as a direct tool on the next request.
import { createMemorySnippetStorage } from '@tanstack/ai-code-mode-snippets'
const storage = createMemorySnippetStorage()The memory storage is also on the /storage subpath.
codeModeWithSnippets asks a model to pick snippets. To skip that model call, load the snippets yourself and build the tools with the lower-level functions. The ts-code-mode-web example uses this approach.
This example loads every snippet. It uses the fetchWeather tool from the quick setup, moved to ./tools:
import {
chat,
chatParamsFromRequest,
maxIterations,
toServerSentEventsResponse,
} from '@tanstack/ai'
import { createCodeMode } from '@tanstack/ai-code-mode'
import { createNodeIsolateDriver } from '@tanstack/ai-isolate-node'
import {
createAlwaysTrustedStrategy,
createSnippetManagementTools,
createSnippetsSystemPrompt,
snippetsToTools,
} from '@tanstack/ai-code-mode-snippets'
import { createFileSnippetStorage } from '@tanstack/ai-code-mode-snippets/storage'
import { openaiText } from '@tanstack/ai-openai'
import { fetchWeather } from './tools'
const trustStrategy = createAlwaysTrustedStrategy()
const storage = createFileSnippetStorage({
directory: './.snippets',
trustStrategy,
})
const driver = createNodeIsolateDriver()
export async function POST(request: Request) {
const { messages, threadId, runId } = await chatParamsFromRequest(request)
// 1. The Code Mode tool and prompt
const { tool: codeModeTool, systemPrompt: codeModePrompt } = createCodeMode({
driver,
tools: [fetchWeather],
})
// 2. Load every snippet and turn each one into a tool
const allSnippets = await storage.loadAll()
const snippetIndex = await storage.loadIndex()
const snippetTools = snippetsToTools({
snippets: allSnippets,
driver,
tools: [fetchWeather],
storage,
})
// 3. The search, get, and register tools
const managementTools = createSnippetManagementTools({
storage,
trustStrategy,
})
// 4. The prompt that documents the snippet library
const snippetsPrompt = createSnippetsSystemPrompt({
selectedSnippets: allSnippets,
totalSnippetCount: snippetIndex.length,
snippetsAsTools: true,
})
const stream = chat({
adapter: openaiText('gpt-6.1-sol'),
tools: [codeModeTool, ...managementTools, ...snippetTools],
messages,
threadId,
runId,
systemPrompts: ['You are a helpful assistant.', codeModePrompt, snippetsPrompt],
agentLoopStrategy: maxIterations(15),
})
return toServerSentEventsResponse(stream)
}snippetsToTools also takes timeout (default 30000) and memoryLimit (default 128).
Options:
| Option | Type | Description |
|---|---|---|
| config | CodeModeToolConfig | The same options as createCodeMode. |
| adapter | text adapter | The model that picks snippets. A cheap, fast model is enough. |
| snippets.storage | SnippetStorage | Where snippets live. |
| snippets.maxSnippetsInContext | number | Most snippets to load for one request. Default: 5. |
| messages | Array<ModelMessage> | The conversation, for snippet selection. |
| snippetsAsTools | boolean | Exposes picked snippets as direct tools. Default: true. |
Return value:
| Property | Type | Description |
|---|---|---|
| toolsRegistry | ToolRegistry | All the tools. Give them to chat() with tools: toolsRegistry.getTools(). |
| systemPrompt | string | The Code Mode prompt plus the snippet library docs. |
| selectedSnippets | Array<Snippet> | The snippets that the selection model picked. |
On each request, selectRelevantSnippets:
If the model returns invalid JSON, the selection is empty. The request continues with no preloaded snippets, and the model can still search and use snippets with the management tools.
snippetsAsTools (default true) controls how the model calls a picked snippet:
| Mode | How the model calls a snippet | Good | Less good |
|---|---|---|---|
| As tools (true) | A direct tool call to the snippet | Simpler for the model. Shows in the tool-call UI. Validates the input. | One tool per snippet in the tool list. |
| As sandbox functions (false) | await snippet_fetch_data({ ... }) inside execute_typescript | Snippets combine in code. Fewer top-level tools. | The model must write code to use them. |
With true, the system prompt documents each snippet with its schema, usage hints, and an example call. With false, snippets appear as typed snippet_* functions in the sandbox type stubs.
Saving. After useful code runs in execute_typescript, the system prompt tells the model to call register_snippet with these fields:
Running. When the model calls a snippet tool:
With codeModeWithSnippets, code in execute_typescript can call both external_* and snippet_* functions. The code of a snippet tool gets only the external_* functions.
Both storages implement SnippetStorage:
| Method | Description |
|---|---|
| loadIndex() | Loads the metadata of all snippets, with no code. |
| loadAll() | Loads all snippets, code included. |
| get(name) | Gets one snippet by name. |
| save(snippet) | Creates or updates a snippet. |
| delete(name) | Removes a snippet. |
| search(query, options?) | Searches snippets by text. |
| updateStats(name, success) | Records one execution result, for trust tracking. |
The file storage writes this layout:
.snippets/
_index.json # Small catalog for fast loading
fetch_github_stats/
meta.json # Description, schemas, hints, stats
code.ts # TypeScript source
compare_npm_packages/
meta.json
code.tscreateFileSnippetStorage takes directory and an optional trustStrategy (default: createDefaultTrustStrategy()).
A snippet starts untrusted and earns trust with successful runs. The trust level is metadata only. It does not block a run.
import {
createDefaultTrustStrategy,
createAlwaysTrustedStrategy,
createRelaxedTrustStrategy,
createCustomTrustStrategy,
} from '@tanstack/ai-code-mode-snippets'| Strategy | Starts at | Provisional | Trusted |
|---|---|---|---|
| Default | untrusted | 10+ runs, 90%+ success | 100+ runs, 95%+ success |
| Relaxed | untrusted | 3+ runs, 80%+ success | 10+ runs, 90%+ success |
| Always trusted | trusted | n/a | n/a |
| Custom | You choose | You choose | You choose |
const strategy = createCustomTrustStrategy({
initialLevel: 'untrusted',
provisionalThreshold: { executions: 5, successRate: 0.85 },
trustedThreshold: { executions: 50, successRate: 0.95 },
})Snippets send these events, next to the Code Mode events. Showing Code Mode in the UI shows how to catch them.
| Event | When | Payload |
|---|---|---|
| code_mode:snippet_call | A snippet tool starts | { snippet, input, timestamp } |
| code_mode:snippet_result | A snippet tool succeeds | { snippet, result, duration, timestamp } |
| code_mode:snippet_error | A snippet tool fails | { snippet, error, duration, timestamp } |
| snippet:registered | register_snippet saves a new snippet | { id, name, description, timestamp } |