├── e2e-projects
└── nextjs
│ ├── .gitignore
│ ├── app
│ ├── layout.tsx
│ ├── PrismicToolbar
│ │ └── page.tsx
│ ├── PrismicLink
│ │ ├── client
│ │ │ └── page.tsx
│ │ └── page.tsx
│ ├── PrismicImage
│ │ ├── client
│ │ │ ├── page.tsx
│ │ │ └── ClientTest.tsx
│ │ └── page.tsx
│ ├── PrismicText
│ │ └── page.tsx
│ ├── PrismicTable
│ │ └── page.tsx
│ ├── SliceZone
│ │ └── page.tsx
│ └── PrismicRichText
│ │ └── page.tsx
│ ├── next.config.ts
│ ├── package.json
│ ├── prismicio.ts
│ ├── tsconfig.json
│ └── prismic-types.d.ts
├── tests
├── infra
│ ├── index.ts
│ ├── content
│ │ ├── index.ts
│ │ ├── table.ts
│ │ ├── link.ts
│ │ ├── page.ts
│ │ ├── image.ts
│ │ └── richtext.ts
│ ├── teardown.ts
│ ├── setup.ts
│ ├── test.ts
│ └── client.ts
├── PrismicToolbar.spec.ts
├── PrismicText.spec.ts
├── PrismicTable.spec.ts
├── SliceZone.spec.ts
├── PrismicLink.spec.ts
├── PrismicImage.spec.ts
└── PrismicRichText.spec.ts
├── examples
├── with-typescript
│ ├── package.json
│ ├── README.md
│ └── index.tsx
├── custom-slicezone-props
│ ├── package.json
│ ├── README.md
│ ├── types.ts
│ └── index.tsx
└── router-link
│ ├── package.json
│ ├── README.md
│ └── index.tsx
├── .size-limit.ts
├── .github
├── ISSUE_TEMPLATE
│ ├── config.yml
│ ├── feature_request.md
│ └── bug_report.md
└── workflows
│ ├── prerelease-pr-cleanup.yml
│ ├── ci.yml
│ ├── prerelease-canary.yml
│ └── prerelease-pr.yml
├── .gitattributes
├── .editorconfig
├── .versionrc
├── tsconfig.json
├── .env.test.example
├── .prettierrc
├── src
├── lib
│ └── devMsg.ts
├── index.ts
├── PrismicToolbar.tsx
├── PrismicText.tsx
├── PrismicTable.tsx
├── PrismicImage.tsx
├── PrismicLink.tsx
├── SliceZone.tsx
└── PrismicRichText.tsx
├── .gitignore
├── .prettierignore
├── vite.config.ts
├── messages
├── classname-is-not-a-valid-prop.md
├── prismictext-works-only-with-rich-text-and-title-fields.md
├── alt-must-be-an-empty-string.md
└── missing-link-properties.md
├── eslint.config.mjs
├── playwright.config.ts
├── package.json
├── README.md
├── CONTRIBUTING.md
└── LICENSE
/e2e-projects/nextjs/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /.next/
3 | /out/
4 | next-env.d.ts
5 |
--------------------------------------------------------------------------------
/tests/infra/index.ts:
--------------------------------------------------------------------------------
1 | export { expect } from "@playwright/test";
2 | export { test } from "./test";
3 |
--------------------------------------------------------------------------------
/examples/with-typescript/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "dependencies": {
4 | "@prismicio/react": "../../src"
5 | },
6 | "devDependencies": {
7 | "@prismicio/client": "^7.11.0"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/tests/infra/content/index.ts:
--------------------------------------------------------------------------------
1 | export * as image from "./image";
2 | export * as link from "./link";
3 | export * as page from "./page";
4 | export * as richText from "./richtext";
5 | export * as table from "./table";
6 |
--------------------------------------------------------------------------------
/examples/custom-slicezone-props/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "dependencies": {
4 | "@prismicio/react": "../../src"
5 | },
6 | "devDependencies": {
7 | "@prismicio/client": "^7.5.0"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/.size-limit.ts:
--------------------------------------------------------------------------------
1 | import type { SizeLimitConfig } from "size-limit";
2 | import { exports } from "./package.json";
3 |
4 | module.exports = [
5 | {
6 | name: "@prismicio/react",
7 | path: exports["."].default,
8 | },
9 | ] satisfies SizeLimitConfig;
10 |
--------------------------------------------------------------------------------
/e2e-projects/nextjs/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode } from "react";
2 |
3 | export default function RootLayout({ children }: { children: ReactNode }) {
4 | return (
5 |
6 |
{children}
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/examples/router-link/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "dependencies": {
4 | "@prismicio/react": "../../src",
5 | "react-router-dom": "^5.2.0"
6 | },
7 | "devDependencies": {
8 | "@prismicio/client": "^7.5.0",
9 | "@types/react-router-dom": "^5.1.8"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: 👪 Prismic Community Forum
4 | url: https://community.prismic.io
5 | about: Ask a question about the package or raise an issue directly related to Prismic. You will usually get support there more quickly!
6 |
--------------------------------------------------------------------------------
/examples/router-link/README.md:
--------------------------------------------------------------------------------
1 | # Router Link
2 |
3 | This example shows how to use a router-specific Link component with ``. This is helpful when links within your app need to use a special component for internal Links. `react-router-dom`, for example, requires using its ` ` component.
4 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # asserts everything is text
2 | * text eol=lf
3 |
4 | # treats lock files as binaries to prevent merge headache
5 | package-lock.json -diff
6 | yarn.lock -diff
7 |
8 | # treats assets as binaries
9 | *.png binary
10 | *.jpg binary
11 | *.jpeg binary
12 | *.gif binary
13 | *.ico binary
14 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = tab
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
14 | [*.yml]
15 | indent_style = space
16 | indent_size = 2
17 |
--------------------------------------------------------------------------------
/e2e-projects/nextjs/app/PrismicToolbar/page.tsx:
--------------------------------------------------------------------------------
1 | import { PrismicToolbar } from "@prismicio/react";
2 |
3 | import { createClient } from "@/prismicio";
4 |
5 | export default async function Page() {
6 | const client = await createClient();
7 |
8 | return ;
9 | }
10 |
--------------------------------------------------------------------------------
/examples/custom-slicezone-props/README.md:
--------------------------------------------------------------------------------
1 | # Custom `SliceZone` Props
2 |
3 | This example shows how to pass custom props to Slice Zone components. This is helpful when your Slice components require data not contained with a Slice. It can also be helpful if your Slice component was not written specifically to accept a Slice as a prop.
4 |
--------------------------------------------------------------------------------
/e2e-projects/nextjs/next.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from "next";
2 | import { fileURLToPath } from "node:url";
3 |
4 | const nextConfig: NextConfig = {
5 | outputFileTracingRoot: fileURLToPath(new URL("../..", import.meta.url)),
6 | images: {
7 | remotePatterns: [{ hostname: "images.prismic.io" }],
8 | },
9 | };
10 |
11 | export default nextConfig;
12 |
--------------------------------------------------------------------------------
/.versionrc:
--------------------------------------------------------------------------------
1 | {
2 | "types": [
3 | {
4 | "type": "feat",
5 | "section": "Features"
6 | },
7 | {
8 | "type": "fix",
9 | "section": "Bug Fixes"
10 | },
11 | {
12 | "type": "refactor",
13 | "section": "Refactor"
14 | },
15 | {
16 | "type": "docs",
17 | "section": "Documentation"
18 | },
19 | {
20 | "type": "chore",
21 | "section": "Chore"
22 | }
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/e2e-projects/nextjs/app/PrismicLink/client/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useState } from "react";
4 | import { PrismicLink } from "@prismicio/react";
5 |
6 | export default function Page() {
7 | const [ref, setRef] = useState(null);
8 |
9 | return (
10 |
11 | tagname: {ref?.tagName}
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/examples/with-typescript/README.md:
--------------------------------------------------------------------------------
1 | # With TypeScript
2 |
3 | This example shows how `@prismicio/react` is used in TypeScript projects. `@prismicio/react` is written in TypeScript using types from [`@prismicio/client`](https://github.com/prismicio/prismic-client).
4 |
5 | You can write types for your documents using `PrismicDocument`. Components from `@prismicio/react` will type check against your document and its fields.
6 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "skipLibCheck": true,
5 |
6 | "target": "esnext",
7 | "module": "esnext",
8 | "moduleResolution": "bundler",
9 | "resolveJsonModule": true,
10 |
11 | "jsx": "react-jsx",
12 | "lib": ["esnext", "dom"],
13 |
14 | "declaration": true,
15 | "outDir": "./dist",
16 |
17 | "types": ["react"]
18 | },
19 | "exclude": ["node_modules", "dist", "e2e-projects"]
20 | }
21 |
--------------------------------------------------------------------------------
/.env.test.example:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # The following environment variables are used to run Playwright tests.
3 | # Create a specific account for testing to avoid issues.
4 | ###############################################################################
5 |
6 | # The email address for your Prismic account.
7 | PLAYWRIGHT_PRISMIC_EMAIL=
8 | # The password to your Prismic account.
9 | PLAYWRIGHT_PRISMIC_PASSWORD=
10 |
--------------------------------------------------------------------------------
/e2e-projects/nextjs/app/PrismicImage/client/page.tsx:
--------------------------------------------------------------------------------
1 | import { isFilled } from "@prismicio/client";
2 | import assert from "assert";
3 |
4 | import { createClient } from "@/prismicio";
5 | import { ClientTest } from "./ClientTest";
6 |
7 | export default async function Page() {
8 | const client = await createClient();
9 | const { data: tests } = await client.getSingle("image_test");
10 |
11 | assert(isFilled.image(tests.filled));
12 |
13 | return ;
14 | }
15 |
--------------------------------------------------------------------------------
/tests/PrismicToolbar.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from "./infra";
2 |
3 | test.beforeEach(async ({ page }) => {
4 | await page.goto("/PrismicToolbar");
5 | });
6 |
7 | test("adds the Prismic toolbar", async ({ appPage }) => {
8 | await expect(appPage.toolbarIframe).toHaveCount(1);
9 | });
10 |
11 | test("includes the repository name in the script element", async ({
12 | appPage,
13 | }) => {
14 | await expect(appPage.toolbarScript).toHaveAttribute(
15 | "data-repository-name",
16 | appPage.repository.domain,
17 | );
18 | });
19 |
--------------------------------------------------------------------------------
/e2e-projects/nextjs/app/PrismicImage/client/ClientTest.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useState } from "react";
4 | import { ImageField } from "@prismicio/client";
5 | import { PrismicImage } from "@prismicio/react";
6 |
7 | export function ClientTest(props: { field: ImageField }) {
8 | const { field } = props;
9 |
10 | const [ref, setRef] = useState(null);
11 |
12 | return (
13 |
14 |
15 | tagname: {ref?.tagName}
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/tests/infra/teardown.ts:
--------------------------------------------------------------------------------
1 | import { STORAGE_STATE } from "../../playwright.config";
2 | import { test as teardown } from "./test";
3 |
4 | teardown("delete repo", async ({ page, prismic }) => {
5 | const cookies = await page.context().cookies();
6 | const repoName = cookies.find((c) => c.name === "repository-name")?.value;
7 | if (!repoName) return;
8 | const repo = prismic.getRepo(repoName);
9 | await repo.delete();
10 | await page.context().clearCookies({ name: "repository-name" });
11 | await page.context().storageState({ path: STORAGE_STATE });
12 | });
13 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["prettier-plugin-jsdoc"],
3 | "jsdocSeparateReturnsFromParam": true,
4 | "jsdocSeparateTagGroups": true,
5 | "jsdocSingleLineComment": false,
6 | "tsdoc": true,
7 | "printWidth": 80,
8 | "useTabs": true,
9 | "semi": true,
10 | "singleQuote": false,
11 | "quoteProps": "as-needed",
12 | "jsxSingleQuote": false,
13 | "trailingComma": "all",
14 | "bracketSpacing": true,
15 | "bracketSameLine": false,
16 | "arrowParens": "always",
17 | "requirePragma": false,
18 | "insertPragma": false,
19 | "htmlWhitespaceSensitivity": "css",
20 | "endOfLine": "lf"
21 | }
22 |
--------------------------------------------------------------------------------
/e2e-projects/nextjs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nextjs",
3 | "private": true,
4 | "scripts": {
5 | "dev": "next dev --port=4321 --turbo",
6 | "build": "next build",
7 | "start": "next start --port=4321"
8 | },
9 | "dependencies": {
10 | "@prismicio/client": "^7.16.0",
11 | "@prismicio/react": "*",
12 | "next": "15.1.9",
13 | "react": "19.2.1",
14 | "react-dom": "19.2.1"
15 | },
16 | "devDependencies": {
17 | "@prismicio/types-internal": "^3.6.0",
18 | "@types/node": "^22",
19 | "@types/react": "^19",
20 | "@types/react-dom": "^19",
21 | "typescript": "^5"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/lib/devMsg.ts:
--------------------------------------------------------------------------------
1 | import { version } from "../../package.json";
2 |
3 | /**
4 | * Returns a `prismic.dev/msg` URL for a given message slug.
5 | *
6 | * @example
7 | *
8 | * ```ts
9 | * devMsg("missing-param");
10 | * // => "https://prismic.dev/msg/react/v1.2.3/missing-param"
11 | * ```
12 | *
13 | * @param slug - Slug for the message. This corresponds to a Markdown file in
14 | * the Git repository's `/messages` directory.
15 | *
16 | * @returns The `prismic.dev/msg` URL for the given slug.
17 | */
18 | export function devMsg(slug: string) {
19 | return `https://prismic.dev/msg/react/v${version}/${slug}`;
20 | }
21 |
--------------------------------------------------------------------------------
/e2e-projects/nextjs/prismicio.ts:
--------------------------------------------------------------------------------
1 | import * as prismic from "@prismicio/client";
2 | import { cookies } from "next/headers";
3 | import assert from "node:assert";
4 |
5 | export async function createClient(config: prismic.ClientConfig = {}) {
6 | const cookieJar = await cookies();
7 | const repositoryName = cookieJar.get("repository-name")?.value;
8 | assert(repositoryName, "A repository-name cookie is required.");
9 |
10 | const client = prismic.createClient(repositoryName, {
11 | routes: [{ type: "page", path: "/page" }],
12 | fetchOptions: { cache: "no-store" },
13 | ...config,
14 | });
15 |
16 | return client;
17 | }
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # custom
2 | dist
3 | examples/**/package-lock.json
4 | *.tgz
5 |
6 | # os
7 | .DS_Store
8 | ._*
9 |
10 | # node
11 | logs
12 | *.log
13 | node_modules
14 |
15 | # yarn
16 | yarn-debug.log*
17 | yarn-error.log*
18 | lerna-debug.log*
19 | .yarn-integrity
20 | yarn.lock
21 |
22 | # npm
23 | npm-debug.log*
24 |
25 | # tests
26 | coverage
27 | .eslintcache
28 | .nyc_output
29 |
30 | # .env
31 | .env
32 | .env.test
33 | .env*.local
34 |
35 | # vscode
36 | .vscode/*
37 | !.vscode/tasks.json
38 | !.vscode/launch.json
39 | *.code-workspace
40 | /test-results/
41 | /playwright-report/
42 | /blob-report/
43 | /playwright/.cache/
44 | /tests/infra/.storage-state.json
45 |
--------------------------------------------------------------------------------
/e2e-projects/nextjs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # custom
2 | dist
3 | examples/**/package-lock.json
4 | *.tgz
5 | CHANGELOG.md
6 | e2e-projects/**/.next
7 |
8 | # os
9 | .DS_Store
10 | ._*
11 |
12 | # node
13 | logs
14 | *.log
15 | node_modules
16 |
17 | # yarn
18 | yarn-debug.log*
19 | yarn-error.log*
20 | lerna-debug.log*
21 | .yarn-integrity
22 | yarn.lock
23 |
24 | # npm
25 | npm-debug.log*
26 |
27 | # tests
28 | coverage
29 | .eslintcache
30 | .nyc_output
31 |
32 | # .env
33 | .env
34 | .env.test
35 | .env*.local
36 |
37 | # vscode
38 | .vscode/*
39 | !.vscode/tasks.json
40 | !.vscode/launch.json
41 | *.code-workspace
42 | /test-results/
43 | /playwright-report/
44 | /blob-report/
45 | /playwright/.cache/
46 | /tests/.storage-state.json
47 |
--------------------------------------------------------------------------------
/examples/custom-slicezone-props/types.ts:
--------------------------------------------------------------------------------
1 | import * as prismic from "@prismicio/client";
2 |
3 | export type HeroSlice = prismic.SharedSlice<
4 | "hero",
5 | prismic.SharedSliceVariation<
6 | "default",
7 | {
8 | heading: prismic.KeyTextField;
9 | buttonText: prismic.KeyTextField;
10 | cards: prismic.GroupField<{
11 | title: prismic.KeyTextField;
12 | content: prismic.KeyTextField;
13 | }>;
14 | }
15 | >
16 | >;
17 |
18 | export type CallToActionSlice = prismic.SharedSlice<
19 | "call_to_action",
20 | prismic.SharedSliceVariation<
21 | "default",
22 | {
23 | text: prismic.KeyTextField;
24 | }
25 | >
26 | >;
27 |
28 | export type Slices = HeroSlice | CallToActionSlice;
29 |
30 | export type ExampleSliceZone = prismic.SliceZone;
31 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import react from "@vitejs/plugin-react";
3 | import typescript from "@rollup/plugin-typescript";
4 | import preserveDirectives from "rollup-preserve-directives";
5 |
6 | import { dependencies, peerDependencies } from "./package.json";
7 |
8 | export default defineConfig({
9 | plugins: [react()],
10 | build: {
11 | lib: {
12 | entry: {
13 | index: "./src/index.ts",
14 | },
15 | formats: ["es"],
16 | },
17 | minify: false,
18 | sourcemap: true,
19 | rollupOptions: {
20 | output: {
21 | preserveModules: true,
22 | preserveModulesRoot: "./src",
23 | },
24 | external: [
25 | ...Object.keys(dependencies),
26 | ...Object.keys(peerDependencies),
27 | ].map((name) => new RegExp(`^${name}(?:/.*)?$`)),
28 | plugins: [typescript({ rootDir: "./src" }), preserveDirectives()],
29 | },
30 | },
31 | });
32 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 🙋♀️ Feature request
3 | about: Suggest an idea or enhancement for the package.
4 | title: ""
5 | labels: "enhancement"
6 | assignees: ""
7 | ---
8 |
9 |
10 |
11 | ### Is your feature request related to a problem? Please describe.
12 |
13 |
14 |
15 | ### Describe the solution you'd like
16 |
17 |
18 |
19 | ### Describe alternatives you've considered
20 |
21 |
22 |
23 | ### Additional context
24 |
25 |
26 |
--------------------------------------------------------------------------------
/examples/router-link/index.tsx:
--------------------------------------------------------------------------------
1 | import * as prismic from "@prismicio/client";
2 | import { PrismicLink, LinkProps } from "@prismicio/react";
3 | import { Link } from "react-router-dom";
4 |
5 | // This is an example Link field value. It contains a URL internal to the app.
6 | const field: prismic.LinkField = {
7 | link_type: prismic.LinkType.Web,
8 | url: "/internal-url",
9 | };
10 |
11 | // This React component acts as a "shim" to convert the `href` prop provided by
12 | // `` to the `to` prop required by react-router-dom's ` `.
13 | const LinkShim = ({ href, ...props }: LinkProps) => {
14 | return ;
15 | };
16 |
17 | // We render the Link field using ``. Since the field contains an
18 | // internal URL, react-router-dom's ` ` component will render.
19 | export const App = () => {
20 | return (
21 |
22 |
23 |
24 | );
25 | };
26 |
--------------------------------------------------------------------------------
/messages/classname-is-not-a-valid-prop.md:
--------------------------------------------------------------------------------
1 | # `className` is not a valid prop
2 |
3 | `` and `` do not accept a `className` prop. These components render an array of React components and do not have a wrapping element.
4 |
5 | To add a `className` as a wrapper around the output of `` or ``, add a wrapper element with the `className`.
6 |
7 | ```tsx
8 | // ✅ Correct
9 |
12 | ```
13 |
14 | ```tsx
15 | // ❌ Incorrect
16 |
17 | ```
18 |
19 | To add a `className` to a specific block type when using ``, provide a custom component.
20 |
21 | ```tsx
22 | (
26 | {children}
27 | ),
28 | }}
29 | />
30 | ```
31 |
--------------------------------------------------------------------------------
/.github/workflows/prerelease-pr-cleanup.yml:
--------------------------------------------------------------------------------
1 | name: prerelease-pr-cleanup
2 |
3 | permissions:
4 | contents: read
5 | id-token: write
6 |
7 | on:
8 | pull_request:
9 | types: [closed]
10 |
11 | jobs:
12 | cleanup:
13 | if: github.repository_owner == 'prismicio'
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v6
17 | - uses: actions/setup-node@v6
18 | with:
19 | node-version: 24
20 | registry-url: "https://registry.npmjs.org"
21 | - name: "Deprecate PR prerelease"
22 | run: |
23 | PACKAGE_NAME=$(jq -r ".name" package.json)
24 | TAG="pr-${{ github.event.number }}"
25 | VERSION=$(npm view "$PACKAGE_NAME" dist-tags."$TAG" 2>/dev/null || echo "")
26 | if [ -n "$VERSION" ]; then
27 | npm deprecate "$PACKAGE_NAME@$VERSION" "PR ${{ github.event.number }} was closed"
28 | npm dist-tag rm "$PACKAGE_NAME" "$TAG"
29 | fi
30 | env:
31 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
32 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 🚨 Bug report
3 | about: Report a bug report to help improve the package.
4 | title: ""
5 | labels: "bug"
6 | assignees: ""
7 | ---
8 |
9 |
16 |
17 | ### Versions
18 |
19 | - `@prismicio/react`:
20 | - `react`:
21 | - Node.js:
22 |
23 | ### Reproduction
24 |
25 |
26 |
27 |
28 | Additional Details
29 |
30 |
31 |
32 |
33 | ### Steps to reproduce
34 |
35 | ### What is expected?
36 |
37 | ### What is actually happening?
38 |
--------------------------------------------------------------------------------
/tests/PrismicText.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from "./infra";
2 |
3 | test.beforeEach(async ({ page }) => {
4 | await page.goto("/PrismicText");
5 | });
6 |
7 | test("renders text from rich text", async ({ page }) => {
8 | const text = page.getByTestId("filled");
9 | await expect(text).toContainText("foo bar");
10 | });
11 |
12 | test("renders null when passed an empty field", async ({ page }) => {
13 | const text = page.getByTestId("empty");
14 | await expect(text).toBeEmpty();
15 | });
16 |
17 | test("renders fallback when passed an empty field", async ({ page }) => {
18 | const text = page.getByTestId("fallback");
19 | await expect(text).toContainText("foo");
20 | });
21 |
22 | test("renders null when passed a string field", async ({ page }) => {
23 | const keytext = page.getByTestId("keytext");
24 | await expect(keytext).toBeEmpty();
25 | const select = page.getByTestId("select");
26 | await expect(select).toBeEmpty();
27 | });
28 |
29 | test("supports custom separator", async ({ page }) => {
30 | const text = page.getByTestId("custom-separator");
31 | await expect(text).toContainText("fooxbar");
32 | });
33 |
--------------------------------------------------------------------------------
/messages/prismictext-works-only-with-rich-text-and-title-fields.md:
--------------------------------------------------------------------------------
1 | # `` works only with Rich Text and Title fields
2 |
3 | `` works only with [Rich Text and Title fields][rich-text-title-field]. It renders the field's value as plain text (i.e. with no formatting, paragraphs, or headings).
4 |
5 | ```tsx
6 | // Will render a plain text version of the Rich Text field's value.
7 |
8 | ```
9 |
10 | Other text-based field types, such as [Key Text][key-text-field] and [Select][select-field], cannot be rendered using ``.
11 |
12 | Since Key Text and Select field values are already plain text, you can render them inline without a special component.
13 |
14 | ```tsx
15 | // Will render the Key Text field's value.
16 | {doc.data.keyTextField}
17 |
18 | // Will render the Select field's value.
19 | {doc.data.selectField}
20 | ```
21 |
22 | [rich-text-title-field]: https://prismic.io/docs/core-concepts/rich-text-title
23 | [key-text-field]: https://prismic.io/docs/core-concepts/key-text
24 | [select-field]: https://prismic.io/docs/core-concepts/select
25 |
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | import eslint from "@eslint/js";
4 | import tseslint from "typescript-eslint";
5 | import prettier from "eslint-plugin-prettier/recommended";
6 | import tsdoc from "eslint-plugin-tsdoc";
7 | import react from "eslint-plugin-react";
8 | import reactHooks from "eslint-plugin-react-hooks";
9 |
10 | export default tseslint.config(
11 | {
12 | ignores: ["dist/", "playwright-report/", "**/.next/"],
13 | },
14 | eslint.configs.recommended,
15 | tseslint.configs.recommended,
16 | prettier,
17 | react.configs.flat.recommended,
18 | react.configs.flat["jsx-runtime"],
19 |
20 | {
21 | settings: {
22 | react: {
23 | version: "detect",
24 | },
25 | },
26 | plugins: {
27 | tsdoc,
28 | // @ts-expect-error - Incompatible types
29 | "react-hooks": reactHooks,
30 | },
31 | // @ts-expect-error - Incompatible types
32 | rules: {
33 | ...reactHooks.configs.recommended.rules,
34 | "@typescript-eslint/no-unused-vars": [
35 | "error",
36 | {
37 | argsIgnorePattern: "^_",
38 | varsIgnorePattern: "^_",
39 | },
40 | ],
41 | "tsdoc/syntax": "warn",
42 | },
43 | },
44 | );
45 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export { PrismicLink } from "./PrismicLink.js";
2 | export type { PrismicLinkProps, LinkProps } from "./PrismicLink.js";
3 |
4 | export { PrismicTable } from "./PrismicTable.js";
5 | export type { PrismicTableProps } from "./PrismicTable.js";
6 |
7 | export { PrismicText } from "./PrismicText.js";
8 | export type { PrismicTextProps } from "./PrismicText.js";
9 |
10 | export { PrismicRichText } from "./PrismicRichText.js";
11 | export type {
12 | PrismicRichTextProps,
13 | JSXMapSerializer,
14 | JSXFunctionSerializer,
15 | } from "./PrismicRichText.js";
16 |
17 | export { Element } from "@prismicio/client/richtext";
18 |
19 | export { PrismicImage } from "./PrismicImage.js";
20 | export type { PrismicImageProps } from "./PrismicImage.js";
21 |
22 | export { SliceZone, TODOSliceComponent } from "./SliceZone.js";
23 | export type {
24 | SliceComponentProps,
25 | SliceComponentType,
26 | SliceLike,
27 | SliceLikeGraphQL,
28 | SliceLikeRestV2,
29 | SliceZoneComponents,
30 | SliceZoneLike,
31 | SliceZoneProps,
32 | } from "./SliceZone.js";
33 |
34 | export { PrismicToolbar } from "./PrismicToolbar.js";
35 | export type { PrismicToolbarProps } from "./PrismicToolbar.js";
36 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: ci
2 |
3 | on: push
4 |
5 | jobs:
6 | ci:
7 | name: Prepare (${{ matrix.os}}, Node.js ${{ matrix.node }})
8 |
9 | strategy:
10 | matrix:
11 | os: [ubuntu-latest]
12 | node: [18, 20, 22]
13 | fail-fast: false
14 |
15 | runs-on: ${{ matrix.os }}
16 |
17 | steps:
18 | - uses: actions/checkout@v4
19 | - uses: actions/setup-node@v4
20 | with:
21 | node-version: ${{ matrix.node }}
22 | registry-url: "https://registry.npmjs.org/"
23 | - run: npm ci
24 | - run: npx playwright install --with-deps
25 | - run: npm run lint
26 | - run: npm run build
27 | - uses: andresz1/size-limit-action@v1
28 | if: ${{ github.event_name == 'pull_request' && matrix.os == 'ubuntu-latest' && matrix.node == 22 }}
29 | with:
30 | github_token: ${{ secrets.GITHUB_TOKEN }}
31 | - run: npm run e2e
32 | env:
33 | PLAYWRIGHT_PRISMIC_EMAIL: ${{ secrets.PLAYWRIGHT_PRISMIC_EMAIL }}
34 | PLAYWRIGHT_PRISMIC_PASSWORD: ${{ secrets.PLAYWRIGHT_PRISMIC_PASSWORD }}
35 | - uses: actions/upload-artifact@v4
36 | if: ${{ !cancelled() }}
37 | with:
38 | name: playwright-report (${{ matrix.os }}, Node.js ${{ matrix.node }})
39 | path: playwright-report/
40 | retention-days: 30
41 |
--------------------------------------------------------------------------------
/.github/workflows/prerelease-canary.yml:
--------------------------------------------------------------------------------
1 | name: prerelease-canary
2 |
3 | permissions:
4 | contents: read
5 | id-token: write
6 |
7 | on:
8 | push:
9 | branches:
10 | - master
11 |
12 | jobs:
13 | publish:
14 | if: github.repository_owner == 'prismicio'
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: actions/checkout@v6
18 | - uses: actions/setup-node@v6
19 | with:
20 | node-version: 24
21 | registry-url: "https://registry.npmjs.org"
22 | - name: "Deprecate previous canary"
23 | run: |
24 | PACKAGE_NAME=$(jq -r ".name" package.json)
25 | PREVIOUS_VERSION=$(npm view "$PACKAGE_NAME" dist-tags.canary 2>/dev/null)
26 | if [ -n "$PREVIOUS_VERSION" ]; then
27 | npm deprecate "$PACKAGE_NAME@$PREVIOUS_VERSION" "Replaced by newer canary version"
28 | fi
29 | env:
30 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
31 | - run: npm ci
32 | - name: "Generate new version"
33 | run: |
34 | SHORT_SHA=$(git rev-parse --short HEAD)
35 | CURRENT_VERSION=$(jq -r '.version' package.json)
36 | VERSION="${CURRENT_VERSION}-canary.${SHORT_SHA}"
37 | npm version $VERSION --no-git-tag-version
38 | - run: npm publish --provenance --tag canary
39 | env:
40 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
41 |
--------------------------------------------------------------------------------
/e2e-projects/nextjs/app/PrismicText/page.tsx:
--------------------------------------------------------------------------------
1 | import { isFilled } from "@prismicio/client";
2 | import { PrismicText } from "@prismicio/react";
3 | import assert from "assert";
4 |
5 | import { createClient } from "@/prismicio";
6 |
7 | export default async function Page() {
8 | const client = await createClient();
9 | const { data: tests } = await client.getSingle("rich_text_test");
10 |
11 | assert(isFilled.richText(tests.filled));
12 | assert(!isFilled.richText(tests.empty));
13 | assert(isFilled.keyText(tests.keytext));
14 | assert(isFilled.select(tests.select));
15 |
16 | return (
17 | <>
18 |
21 |
22 |
25 |
26 |
29 |
30 |
31 | {/* @ts-expect-error - We are purposely providing an field. */}
32 |
33 |
34 |
35 | {/* @ts-expect-error - We are purposely providing an field. */}
36 |
37 |
38 |
39 |
42 | >
43 | );
44 | }
45 |
--------------------------------------------------------------------------------
/.github/workflows/prerelease-pr.yml:
--------------------------------------------------------------------------------
1 | name: prerelease-pr
2 |
3 | permissions:
4 | contents: read
5 | id-token: write
6 |
7 | on:
8 | pull_request:
9 |
10 | jobs:
11 | publish:
12 | if: github.repository_owner == 'prismicio'
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v6
16 | - uses: actions/setup-node@v6
17 | with:
18 | node-version: 24
19 | registry-url: "https://registry.npmjs.org"
20 | - name: "Deprecate previous PR prerelease"
21 | run: |
22 | PACKAGE_NAME=$(jq -r ".name" package.json)
23 | TAG="pr-${{ github.event.number }}"
24 | PREVIOUS_VERSION=$(npm view "$PACKAGE_NAME" dist-tags."$TAG" 2>/dev/null)
25 | if [ -n "$PREVIOUS_VERSION" ]; then
26 | npm deprecate "$PACKAGE_NAME@$PREVIOUS_VERSION" "Replaced by newer prerelease version"
27 | fi
28 | env:
29 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
30 | - run: npm ci
31 | - name: "Generate new version"
32 | run: |
33 | SHORT_SHA=$(git rev-parse --short HEAD)
34 | CURRENT_VERSION=$(jq -r '.version' package.json)
35 | VERSION="${CURRENT_VERSION}-pr.${{ github.event.number }}.${SHORT_SHA}"
36 | npm version $VERSION --no-git-tag-version
37 | - run: npm publish --provenance --tag pr-${{ github.event.number }}
38 | env:
39 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
40 |
--------------------------------------------------------------------------------
/src/PrismicToolbar.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { FC, useEffect } from "react";
4 | import { getToolbarSrc } from "@prismicio/client";
5 |
6 | /** Props for ``. */
7 | export type PrismicToolbarProps = {
8 | /**
9 | * The name of the Prismic repository. For example, `"my-repo"` if the
10 | * repository URL is `my-repo.prismic.io`.
11 | */
12 | repositoryName: string;
13 | };
14 |
15 | /**
16 | * Renders the Prismic Toolbar script to support draft previews.
17 | *
18 | * @example
19 | *
20 | * ```tsx
21 | * ;
22 | * ```
23 | *
24 | * @see Learn how to set up preview functionality and the toolbar's role in preview sessions: {@link https://prismic.io/docs/previews}
25 | */
26 | export const PrismicToolbar: FC = (props) => {
27 | const { repositoryName } = props;
28 |
29 | const src = getToolbarSrc(repositoryName);
30 |
31 | useEffect(() => {
32 | const existingScript = document.querySelector(`script[src="${src}"]`);
33 |
34 | if (!existingScript) {
35 | const script = document.createElement("script");
36 | script.src = src;
37 | script.defer = true;
38 |
39 | // Used to distinguish the toolbar element from other elements.
40 | script.dataset.prismicToolbar = "";
41 | script.dataset.repositoryName = repositoryName;
42 |
43 | // Disable Happy DOM `