Yak Docs
CMS

Prismic CMS

The @yak-io/prismic package adapts your Prismic content into Yak primitives. It produces standard RouteSource, ToolSource, and SchemaSource values that compose with the existing framework SDKs — most commonly @yak-io/nextjs or @yak-io/nuxt.

What you get

  • createPrismicRouteAdapter — turn published Prismic documents into the route manifest the LLM uses for navigation.
  • createPrismicToolAdapter — expose prismic.getByUID, prismic.getAllByType, and prismic.search as tools the assistant can invoke.
  • createPrismicSchemaSource — return a GraphQL schema source describing your content model, so the assistant knows which document types and fields exist.

Installation

npm install @yak-io/prismic @yak-io/javascript @prismicio/client
pnpm add @yak-io/prismic @yak-io/javascript @prismicio/client
yarn add @yak-io/prismic @yak-io/javascript @prismicio/client
bun add @yak-io/prismic @yak-io/javascript @prismicio/client

@prismicio/client (v7+) is a peer dependency. If you want to use the GraphQL schema source, also install graphql.

Quick Start

Create a Prismic client

Wherever you would normally configure Prismic — usually a shared prismicio.ts:

import * as prismic from "@prismicio/client";

export const prismicClient = prismic.createClient("your-repo-name", {
  accessToken: process.env.PRISMIC_ACCESS_TOKEN,
});

Build the adapters

Each factory takes the client plus a small config. Routes need a resolveRoute mapper from document → RouteInfo. Tools need an allowedTypes list — this is the security boundary for what the LLM can read.

import {
  createPrismicRouteAdapter,
  createPrismicToolAdapter,
} from "@yak-io/prismic";
import { prismicClient } from "./prismicio";

export const prismicRoutes = createPrismicRouteAdapter({
  client: prismicClient,
  documentTypes: ["page", "blog_post"],
  resolveRoute: (doc) => ({
    path: `/${doc.uid}`,
    title: doc.data.meta_title ?? doc.data.title,
    description: doc.data.meta_description,
  }),
});

export const prismicTools = createPrismicToolAdapter({
  client: prismicClient,
  allowedTypes: ["page", "blog_post"],
});

Plug them into your Yak handler

See the framework sections below — the exact wiring differs between Next.js and Nuxt, but in both cases you pass the adapters straight into the standard handler config.

Using with Next.js

Pair @yak-io/prismic with @yak-io/nextjs. The Next.js handler accepts route and tool sources as arrays, so you can combine Prismic-sourced routes with the filesystem auto-scan or with other tool adapters in the same handler.

app/api/yak/[[...yak]]/route.ts
import { createNextYakHandler } from "@yak-io/nextjs/server";
import {
  createPrismicRouteAdapter,
  createPrismicToolAdapter,
} from "@yak-io/prismic";
import { prismicClient } from "@/prismicio";

const prismicRoutes = createPrismicRouteAdapter({
  client: prismicClient,
  documentTypes: ["page", "blog_post"],
  resolveRoute: (doc) => ({
    path: `/${doc.uid}`,
    title: doc.data.meta_title ?? doc.data.title,
    description: doc.data.meta_description,
  }),
});

const prismicTools = createPrismicToolAdapter({
  client: prismicClient,
  allowedTypes: ["page", "blog_post"],
});

export const { GET, POST } = createNextYakHandler({
  appDir: "./src/app",
  routes: [prismicRoutes],
  tools: [prismicTools],
});

If you want filesystem routes and Prismic routes, just include both in the array — the handler merges and deduplicates by path:

import { scanRoutes } from "@yak-io/nextjs/server";

export const { GET, POST } = createNextYakHandler({
  routes: [
    () => scanRoutes("./src/app"),
    prismicRoutes,
  ],
  tools: [prismicTools],
});

Schema source on the client

Schema sources flow through the client-side getConfig provider (not the server handler). Wire them into your YakProvider:

app/providers.tsx
"use client";
import { YakProvider, YakWidget } from "@yak-io/nextjs/client";
import { createPrismicSchemaSource } from "@yak-io/prismic";
import { prismicClient } from "@/prismicio";

let cachedSchema: Awaited<ReturnType<typeof createPrismicSchemaSource>> | null = null;

async function getConfig() {
  const res = await fetch("/api/yak");
  const config = await res.json();
  cachedSchema ??= await createPrismicSchemaSource({
    client: prismicClient,
    mode: "graphql",
  });
  return { ...config, schemaSources: [cachedSchema] };
}

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <YakProvider appId={process.env.NEXT_PUBLIC_YAK_APP_ID!} getConfig={getConfig}>
      {children}
      <YakWidget />
    </YakProvider>
  );
}

Cache the introspection result. The GraphQL schema rarely changes between sessions, and you don't want to re-introspect on every chat open.

Using with Nuxt

Pair @yak-io/prismic with @yak-io/nuxt. Nuxt's server side uses Nitro routes; you call the underlying createYakHandler from @yak-io/javascript/server and pass the adapters into it the same way.

server/api/yak.get.ts
import { createYakHandler } from "@yak-io/javascript/server";
import {
  createPrismicRouteAdapter,
  createPrismicToolAdapter,
} from "@yak-io/prismic";
import { prismicClient } from "~/prismicio";

const prismicRoutes = createPrismicRouteAdapter({
  client: prismicClient,
  documentTypes: ["page", "blog_post"],
  resolveRoute: (doc) => ({
    path: `/${doc.uid}`,
    title: doc.data.meta_title ?? doc.data.title,
    description: doc.data.meta_description,
  }),
});

const prismicTools = createPrismicToolAdapter({
  client: prismicClient,
  allowedTypes: ["page", "blog_post"],
});

const { GET } = createYakHandler({
  routes: [prismicRoutes],
  tools: [prismicTools],
});

export default defineEventHandler(async (event) => {
  const request = toWebRequest(event);
  const response = await GET(request);
  return response.json();
});
server/api/yak.post.ts
import { createYakHandler } from "@yak-io/javascript/server";
import { createPrismicToolAdapter } from "@yak-io/prismic";
import { prismicClient } from "~/prismicio";

const { POST } = createYakHandler({
  routes: [],
  tools: [
    createPrismicToolAdapter({
      client: prismicClient,
      allowedTypes: ["page", "blog_post"],
    }),
  ],
});

export default defineEventHandler(async (event) => {
  const request = toWebRequest(event);
  const response = await POST(request);
  return response.json();
});

Schema source on the client

In your .client.ts Nuxt plugin, return the schema source from getConfig:

plugins/yak.client.ts
import { createYakProvider } from "@yak-io/nuxt";
import { createPrismicSchemaSource } from "@yak-io/prismic";
import { prismicClient } from "~/prismicio";

let cachedSchema: Awaited<ReturnType<typeof createPrismicSchemaSource>> | null = null;

export default defineNuxtPlugin((nuxtApp) => {
  const yak = createYakProvider({
    appId: useRuntimeConfig().public.yakAppId,
    getConfig: async () => {
      const config = await $fetch("/api/yak");
      cachedSchema ??= await createPrismicSchemaSource({
        client: prismicClient,
        mode: "graphql",
      });
      return { ...config, schemaSources: [cachedSchema] };
    },
    onToolCall: async (name, args) => {
      const res = await $fetch("/api/yak", { method: "POST", body: { name, args } });
      return res.result;
    },
  });

  nuxtApp.hook("app:mounted", () => yak.mount());
  return { provide: { yak } };
});

API Reference

createPrismicRouteAdapter

const routes = createPrismicRouteAdapter({
  client: prismicClient,
  documentTypes: ["page", "blog_post"],
  resolveRoute: (doc) => ({ path: `/${doc.uid}` }),
});
OptionTypeDescription
clientClientA @prismicio/client instance
documentTypesstring[]Which Prismic custom types to include in the route manifest
resolveRoute(doc) => RouteInfo | nullMap each document to a RouteInfo entry. Return null to skip a document (drafts, hidden pages, etc.)
filtersstring[]Optional additional Prismic filter expressions, appended after the type filter
idstringOptional source identifier (default: "prismic")

createPrismicToolAdapter

const tools = createPrismicToolAdapter({
  client: prismicClient,
  allowedTypes: ["page", "blog_post"],
});
OptionTypeDescription
clientClientA @prismicio/client instance
allowedTypesstring[]Document types the LLM is allowed to query. Required — this is the security boundary
fieldsRecord<string, string[]>Optional per-type field projection. Translated to Prismic's GraphQuery DSL on the wire
idstringOptional source identifier (default: "prismic")

The adapter exposes three tools:

Tool namePurpose
prismic.getByUIDFetch one document by (type, uid)
prismic.getAllByTypeList all documents of a given type (default limit: 20)
prismic.searchFull-text search across allowed types

createPrismicSchemaSource

const schema = await createPrismicSchemaSource({
  client: prismicClient,
  mode: "graphql",
});
OptionTypeDescription
clientClientA @prismicio/client instance
mode"graphql"Currently only GraphQL is supported. Introspects your Prismic repo's /graphql endpoint
namestringOptional override for the schema source name (default: "prismic")
fetchFntypeof fetchOptional fetch override for testing or custom transport

GraphQL mode requires the graphql package to be installed alongside this adapter.

Composing with other sources

Route and tool sources merge cleanly. You can pair Prismic with anything else in the same handler:

import { createNextYakHandler } from "@yak-io/nextjs/server";
import { createTRPCToolAdapter } from "@yak-io/trpc";
import { createPrismicToolAdapter } from "@yak-io/prismic";
import { appRouter, createContext } from "@/server/trpc";
import { prismicClient } from "@/prismicio";

export const { GET, POST } = createNextYakHandler({
  appDir: "./src/app",
  tools: [
    createPrismicToolAdapter({
      client: prismicClient,
      allowedTypes: ["page", "blog_post"],
    }),
    createTRPCToolAdapter({
      router: appRouter,
      createContext: async ({ req }) => createContext({ req }),
      allowedProcedures: ["orders.list", "orders.getById"],
    }),
  ],
});

The route manifest merger dedupes by path, so a Prismic-sourced /about route won't conflict with a filesystem-sourced one.

Security

allowedTypes defines the read surface the LLM has into your Prismic repository. Without it, anyone using the assistant could ask it to fetch internal document types (settings, feature_flags, etc.). Always set this to the minimal list of public content types.

  • Restrict documentTypes (routes) and allowedTypes (tools) to the document types that are genuinely public.
  • Use resolveRoute returning null to filter unpublished drafts out of the route manifest.
  • Tool calls execute server-side via createYakHandler / createNextYakHandler — your Prismic access token never reaches the browser.

On this page