create-puck-app 0.22.2 → 0.22.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -66,11 +66,18 @@ program
66
66
  Explicitly tell the CLI to bootstrap the application using Yarn
67
67
  `
68
68
  )
69
+ .option(
70
+ "--ai",
71
+ `
72
+
73
+ Add Puck AI to the editor (requires a Puck Cloud account)
74
+ `
75
+ )
69
76
  .action(async (_appName, options) => {
70
- const beforeQuestions = [];
77
+ const questions = [];
71
78
 
72
79
  if (!_appName) {
73
- beforeQuestions.push({
80
+ questions.push({
74
81
  type: "input",
75
82
  name: "appName",
76
83
  message: "What is the name of your app?",
@@ -78,26 +85,27 @@ program
78
85
  });
79
86
  }
80
87
 
81
- const questions = [
82
- ...beforeQuestions,
83
- {
84
- type: "list",
85
- name: "recipe",
86
- message: "Which recipe would you like to use?",
87
- required: true,
88
- default: "next",
89
- choices: [
90
- {
91
- name: "Next.js",
92
- value: "next",
93
- },
94
- {
95
- name: "React Router",
96
- value: "react-router",
97
- },
98
- ],
99
- },
100
- {
88
+ questions.push({
89
+ type: "list",
90
+ name: "recipe",
91
+ message: "Which recipe would you like to use?",
92
+ required: true,
93
+ default: "next",
94
+ choices: [
95
+ {
96
+ name: "Next.js",
97
+ value: "next",
98
+ },
99
+ {
100
+ name: "React Router",
101
+ value: "react-router",
102
+ },
103
+ ],
104
+ });
105
+
106
+ // If the user didn't specify the --ai flag, ask them if they want to add Puck AI to the editor.
107
+ if (!options.ai) {
108
+ questions.push({
101
109
  type: "confirm",
102
110
  name: "puckAi",
103
111
  message: `Puck AI (beta) lets you generate pages using your own components. Learn more: ${ansiColors.cyan}https://puckeditor.com/docs/ai/overview${ansiColors.reset}
@@ -105,8 +113,8 @@ program
105
113
  Add Puck AI to the editor? (Requires a Puck Cloud account)`,
106
114
  required: true,
107
115
  default: true,
108
- },
109
- ];
116
+ });
117
+ }
110
118
 
111
119
  const answers = await inquirer.prompt(questions);
112
120
 
@@ -120,7 +128,7 @@ program
120
128
  }
121
129
 
122
130
  const recipe = answers.recipe;
123
- const usesPuckAi = answers.puckAi;
131
+ const usesPuckAi = answers.puckAi || !!options.ai;
124
132
 
125
133
  // Copy template files to the new directory
126
134
  const recipeName = `${recipe}${usesPuckAi ? "-ai" : ""}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-puck-app",
3
- "version": "0.22.2",
3
+ "version": "0.22.3",
4
4
  "author": "Chris Villa <chris@puckeditor.com>",
5
5
  "repository": "puckeditor/puck",
6
6
  "bugs": "https://github.com/puckeditor/puck/issues",
@@ -1,38 +1,112 @@
1
- # `next` recipe
1
+ # Puck + Next.js recipe
2
2
 
3
- The `next` recipe showcases one of the most powerful ways to implement Puck using to provide an authoring tool for any route in your Next app.
3
+ [Puck](https://puckeditor.com) is the open-source visual editor for React.
4
4
 
5
- ## Demonstrates
5
+ This recipe connects Puck to the [Next.js App Router](https://nextjs.org/docs/app), so you can create and edit pages for any route in this app.
6
6
 
7
- - Next.js App Router implementation
8
- - JSON database implementation with HTTP API
9
- - Catch-all routes to use puck for any route on the platform
10
- - Incremental static regeneration (ISR) for all Puck pages
7
+ ## Core concepts
11
8
 
12
- ## Usage
9
+ If you're new to Puck, this section introduces the core concepts you need to know.
13
10
 
14
- Run the generator and enter `next` when prompted
11
+ ### Puck
15
12
 
13
+ The Puck visual editor has three main parts: a config, the editor, and the renderer.
14
+
15
+ #### Config
16
+
17
+ The [config](https://puckeditor.com/docs/integrating-puck/component-configuration) registers the components users can use to build pages in the editor and the fields they can edit.
18
+
19
+ ```tsx
20
+ const config = {
21
+ components: {
22
+ HeadingBlock: {
23
+ fields: {
24
+ title: { type: "text" },
25
+ },
26
+ render: ({ title }) => <h1>{title}</h1>,
27
+ },
28
+ },
29
+ };
16
30
  ```
17
- npx create-puck-app my-app
31
+
32
+ #### The editor
33
+
34
+ The [`<Puck>`](https://puckeditor.com/docs/api-reference/components/puck) component renders the editor. It uses a config, exports [pages as JSON](https://puckeditor.com/docs/api-reference/data-model/data), and accepts initial page data for editing existing pages.
35
+
36
+ ```tsx
37
+ <Puck
38
+ config={config} // The components available to the editor
39
+ data={data} // The page JSON to edit
40
+ onPublish={(data) => {
41
+ // Save data to your database
42
+ }}
43
+ />
18
44
  ```
19
45
 
20
- Start the server
46
+ #### The renderer
21
47
 
48
+ The [`<Render>`](https://puckeditor.com/docs/api-reference/components/render) component renders pages. It expects the page JSON and the config used to create that page.
49
+
50
+ ```tsx
51
+ <Render
52
+ config={config} // The components used to create the page
53
+ data={data} // The page JSON to render
54
+ />
22
55
  ```
23
- yarn dev
56
+
57
+ ## Run the recipe
58
+
59
+ ### 1. Start the development server
60
+
61
+ Run:
62
+
63
+ ```sh
64
+ npm run dev
24
65
  ```
25
66
 
26
- Navigate to the homepage at https://localhost:3000. To edit the homepage, access the Puck editor at https://localhost:3000/edit.
67
+ Once the server is running, navigate to [http://localhost:3000](http://localhost:3000) to view the home page, or [http://localhost:3000/edit](http://localhost:3000/edit) to edit it with Puck.
68
+
69
+ ### 2. Create a page
70
+
71
+ Navigate to [http://localhost:3000/edit](http://localhost:3000/edit), open the `Blocks` tab in the left sidebar and build your page by dragging components onto the canvas.
72
+
73
+ ### 3. Publish the page
74
+
75
+ Once your page is ready, select **Publish** in the header to save the result, then navigate to [http://localhost:3000](http://localhost:3000) to view the published page.
76
+
77
+ You can also create a page at any path by navigating to `/your/path/edit` and publishing it. The route `/your/path` will render the page.
78
+
79
+ ## How it works
80
+
81
+ When a URL ends in `/edit`, [`proxy.ts`](https://nextjs.org/docs/app/api-reference/file-conventions/proxy) sends the request to the Puck editor route (`app/puck/[...puckPath]/page.tsx`). The editor loads the saved page, or starts with an empty page if the path is new.
82
+
83
+ Selecting **Publish** sends the page data to the `/puck/api` endpoint (`app/puck/api/route.ts`). The handler writes the JSON to `database.json` and clears the Next.js cache for that page. The catch-all route (`app/[...puckPath]/page.tsx`) then loads the same data and renders it with [`<Render>`](https://puckeditor.com/docs/api-reference/components/render).
84
+
85
+ The table below shows the files that implement this flow.
86
+
87
+ | File | Purpose |
88
+ | ----------------------------------- | -------------------------------------------------------------------------------------------------- |
89
+ | `puck.config.tsx` | Defines the components, fields, and default props available to Puck. Add your own components here. |
90
+ | `app/puck/[...puckPath]/page.tsx` | Loads page data for the editor. |
91
+ | `app/puck/[...puckPath]/client.tsx` | Renders the editor and publishes changes. |
92
+ | `app/[...puckPath]/page.tsx` | Loads and renders published pages. |
93
+ | `app/puck/api/route.ts` | Saves published pages. |
94
+ | `proxy.ts` | Routes URLs ending in `/edit` to `/puck/[...puckPath]/page.tsx`. |
95
+ | `lib/get-page.ts` | Reads page data from `database.json`. Replace this with your own data fetching logic. |
96
+ | `database.json` | Acts as a local database. Replace this with your own database solution. |
27
97
 
28
- You can do this for any route on the application, **even if the page doesn't exist**. For example, visit https://localhost:3000/hello/world and you'll receive a 404. You can author and publish a page by visiting https://localhost:3000/hello/world/edit. After publishing, go back to the original URL to see your page.
98
+ ## Before deploying to production
29
99
 
30
- ## Using this recipe
100
+ Before deploying this recipe, make sure to:
31
101
 
32
- To adopt this recipe you will need to:
102
+ - **Protect the editor and API.** The `/edit` routes and `/puck/api` endpoint are public by default. Add authentication and authorization so only trusted users can edit or publish pages.
103
+ - **Add your component library.** Replace the example `HeadingBlock` in `puck.config.tsx` with the components and fields your users need.
104
+ - **Use a real database.** Replace `database.json` in `lib/get-page.ts` and `app/puck/api/route.ts`. Local files are not reliable across server instances or serverless deployments.
105
+ - **Choose a rendering strategy.** `app/[...puckPath]/page.tsx` uses `force-static`. Remove it if a page needs request-time data such as headers, cookies, or user sessions.
33
106
 
34
- - **IMPORTANT** Add authentication to `/edit` routes. This can be done by modifying the example API routes in `/app/puck/api/route.ts` and server component in `/app/puck/[...puckPath]/page.tsx`. **If you don't do this, Puck will be completely public.**
35
- - Integrate your database into the API calls in `/app/puck/api/route.ts`
36
- - Implement a custom puck configuration in `puck.config.tsx`
107
+ ## Learn more
37
108
 
38
- By default, this recipe will generate static pages by setting `dynamic` to [`force-static`](https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic) in the `/app/[...puckPath]/page.tsx`. This will strip headers and cookies. If you need dynamic pages, you can delete this.
109
+ - [Puck documentation](https://puckeditor.com/docs)
110
+ - [Getting started with Puck](https://puckeditor.com/docs/getting-started)
111
+ - [Integrating Puck](https://puckeditor.com/docs/integrating-puck/component-configuration)
112
+ - [Puck Discord](https://discord.gg/D9e4E3MQVZ)
@@ -1,60 +1,169 @@
1
- # `next-ai` recipe
1
+ # Puck AI + Next.js recipe
2
2
 
3
- The `next-ai` recipe showcases one of the most powerful ways to combine Puck and [Puck AI](https://puckeditor.com/docs/ai/overview): providing an authoring tool with AI page generation capabilities for any route in your Next app.
3
+ [Puck](https://puckeditor.com) is the open-source visual editor for React. It lets you create page builders that use your own components.
4
4
 
5
- ## Demonstrates
5
+ [Puck AI](https://puckeditor.com/docs/ai/overview) builds on the same principles to let you generate pages by assembling your existing components or creating new ones on the fly, either as a copilot in the editor or headlessly.
6
6
 
7
- - Puck AI integration for generating pages with AI
8
- - Next.js App Router implementation
9
- - JSON database implementation with HTTP API
10
- - Catch-all routes to use puck for any route on the platform
11
- - Incremental static regeneration (ISR) for all Puck pages
7
+ This recipe connects Puck and Puck AI to the [Next.js App Router](https://nextjs.org/docs/app), so you can create and edit pages for any route in this app.
12
8
 
13
- ## Usage
9
+ ## Core concepts
14
10
 
15
- Run the generator and select `Next.js` when prompted
11
+ If you're new to Puck, this section introduces the core concepts you need to know.
16
12
 
17
- ```
18
- npx create-puck-app my-app
13
+ ### Puck
19
14
 
20
- ? Which recipe would you like to use?
21
- ❯ Next.js
22
- ```
15
+ The Puck visual editor has three main parts: a config, the editor, and the renderer.
16
+
17
+ #### Config
23
18
 
24
- Confirm you want to use Puck AI
19
+ The [config](https://puckeditor.com/docs/integrating-puck/component-configuration) registers the components users can use to build pages in the editor and the fields they can edit.
25
20
 
21
+ ```tsx
22
+ const config = {
23
+ components: {
24
+ HeadingBlock: {
25
+ fields: {
26
+ title: { type: "text" },
27
+ },
28
+ render: ({ title }) => <h1>{title}</h1>,
29
+ },
30
+ },
31
+ };
26
32
  ```
27
- ? Would you like to use Puck AI? (Y/n) Y
33
+
34
+ #### The editor
35
+
36
+ The [`<Puck>`](https://puckeditor.com/docs/api-reference/components/puck) component renders the editor. It uses a config, exports [pages as JSON](https://puckeditor.com/docs/api-reference/data-model/data), and accepts initial page data for editing existing pages.
37
+
38
+ ```tsx
39
+ <Puck
40
+ config={config} // The components available to the editor
41
+ data={data} // The page JSON to edit
42
+ onPublish={(data) => {
43
+ // Save data to your database
44
+ }}
45
+ />
28
46
  ```
29
47
 
30
- Start the server
48
+ #### The renderer
49
+
50
+ The [`<Render>`](https://puckeditor.com/docs/api-reference/components/render) component renders pages. It expects the page JSON and the config used to create that page.
31
51
 
52
+ ```tsx
53
+ <Render
54
+ config={config} // The components used to create the page
55
+ data={data} // The page JSON to render
56
+ />
32
57
  ```
33
- cd my-app
34
- yarn dev
58
+
59
+ ### Puck AI
60
+
61
+ This recipe adds Puck AI as a copilot. It has two parts: the AI plugin (browser) and the Cloud Client (server).
62
+
63
+ #### The AI plugin
64
+
65
+ The [AI plugin](https://puckeditor.com/docs/api-reference/ai/ai-plugin/installation) renders the chat in the editor and sends each message to the Cloud Client on your server.
66
+
67
+ ```tsx
68
+ const aiPlugin = createAiPlugin();
69
+
70
+ function Editor() {
71
+ return <Puck plugins={[aiPlugin]} config={config} data={data} />;
72
+ }
35
73
  ```
36
74
 
37
- ### Set up Puck AI
75
+ #### The Cloud Client
38
76
 
39
- Create a [Puck account](https://cloud.puckeditor.com) and [obtain an API key](https://cloud.puckeditor.com/api-keys).
77
+ The [Cloud Client](https://puckeditor.com/docs/api-reference/ai/cloud-client/installation) provides APIs for connecting your server to the Puck cloud. This recipe uses its [`puckHandler`](https://puckeditor.com/docs/api-reference/ai/cloud-client/puck-handler) API, which receives each chat message, forwards it to the Puck cloud, and streams the response back to the plugin in the browser.
40
78
 
41
- Create a `.env.local` file in the root of your project and add your API key:
79
+ ```ts
80
+ const handleRequest = (request: NextRequest) => {
81
+ return puckHandler(request, {
82
+ ai: {
83
+ context: "We are Google. You create Google landing pages.",
84
+ },
85
+ });
86
+ };
42
87
 
88
+ export const DELETE = handleRequest;
89
+ export const GET = handleRequest;
90
+ export const POST = handleRequest;
43
91
  ```
92
+
93
+ #### Puck AI modes
94
+
95
+ Puck AI can build pages in two ways:
96
+
97
+ - **Assembly mode** only builds pages using components from your config.
98
+ - **Design mode** can generate new components when needed.
99
+
100
+ This recipe comes with [Design mode](https://puckeditor.com/docs/api-reference/ai/cloud-client/puck-handler#aidesignmode) enabled out of the box.
101
+
102
+ ## Run the recipe
103
+
104
+ ### 1. Add a Puck API key
105
+
106
+ Start by creating an account, [generating an API key](https://cloud.puckeditor.com/api-keys), and adding it to a `.env.local` file:
107
+
108
+ ```sh
44
109
  PUCK_API_KEY=your-api-key
45
110
  ```
46
111
 
47
- Navigate to the homepage at https://localhost:3000. To edit the homepage, access the Puck editor at https://localhost:3000/edit, and select the AI button in the left navigation bar to generate content for the page using Puck AI.
112
+ ### 2. Start the development server
113
+
114
+ Run:
115
+
116
+ ```sh
117
+ npm run dev
118
+ ```
119
+
120
+ Once the server is running, navigate to [http://localhost:3000](http://localhost:3000) to view the home page, or [http://localhost:3000/edit](http://localhost:3000/edit) to edit it with Puck.
121
+
122
+ ### 3. Create a page with Puck AI
123
+
124
+ Navigate to [http://localhost:3000/edit](http://localhost:3000/edit), click the **AI** button in the left sidebar, enter a prompt, and press Enter.
125
+
126
+ ### 4. Publish the page
127
+
128
+ Once your page is ready, select **Publish** in the header to save the result, then navigate to [http://localhost:3000](http://localhost:3000) to view the published page.
129
+
130
+ You can also create a page at any path by navigating to `/your/path/edit` and publishing it. The route `/your/path` will render the page.
131
+
132
+ ## How it works
133
+
134
+ When a URL ends in `/edit`, [`proxy.ts`](https://nextjs.org/docs/app/api-reference/file-conventions/proxy) sends the request to the Puck editor route (`app/puck/[...puckPath]/page.tsx`). The editor loads the saved page, or starts with an empty page if the path is new.
135
+
136
+ Selecting **Publish** sends the page data to the `/api/pages` endpoint (`app/api/pages/route.ts`). The handler writes the JSON to `database.json` and clears the Next.js cache for that page. The catch-all route (`app/[...puckPath]/page.tsx`) then loads the same data and renders it with [`<Render>`](https://puckeditor.com/docs/api-reference/components/render).
137
+
138
+ The table below shows the files that implement this flow.
139
+
140
+ | File | Purpose |
141
+ | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
142
+ | `puck.config.tsx` | Defines the components, fields, and default props available to Puck and Assembly mode. Add your own components here. |
143
+ | `app/puck/[...puckPath]/page.tsx` | Loads page data for the editor. |
144
+ | `app/puck/[...puckPath]/client.tsx` | Renders the editor with the AI copilot, sends prompts to your server, and publishes changes. |
145
+ | `app/[...puckPath]/page.tsx` | Loads and renders published pages. |
146
+ | `app/api/pages/route.ts` | Saves published pages. |
147
+ | `app/api/puck/[...all]/route.ts` | Handles requests from the AI plugin and configures AI generation. |
148
+ | `proxy.ts` | Routes URLs ending in `/edit` to `/puck/[...puckPath]/page.tsx`. |
149
+ | `lib/get-page.ts` | Reads page data from `database.json`. Replace this with your own data fetching logic. |
150
+ | `database.json` | Acts as a local database. Replace this with your own database solution. |
48
151
 
49
- You can do this for any route on the application, **even if the page doesn't exist**. For example, visit https://localhost:3000/hello/world and you'll receive a 404. You can author and publish a page by visiting https://localhost:3000/hello/world/edit. After publishing, go back to the original URL to see your page.
152
+ ## Before deploying to production
50
153
 
51
- ## Using this recipe
154
+ Before deploying this recipe, make sure to:
52
155
 
53
- To adopt this recipe you will need to:
156
+ - **Protect the editor and APIs.** The `/edit`, `/api/pages`, and `/api/puck` routes are public by default. Add authentication, authorization, and rate limits to protect page data and AI usage.
157
+ - **Add your component library.** Replace the example `HeadingBlock` in `puck.config.tsx` with the components and fields your users need.
158
+ - **Set your business context.** Replace the example Google context in `app/api/puck/[...all]/route.ts` with clear information about your product, audience, and content rules.
159
+ - **Use a real database.** Replace `database.json` in `lib/get-page.ts` and `app/api/pages/route.ts`. Local files are not reliable across server instances or serverless deployments.
160
+ - **Choose a rendering strategy.** `app/[...puckPath]/page.tsx` uses `force-static`. Remove it if a page needs request-time data such as headers, cookies, or user sessions.
54
161
 
55
- - **IMPORTANT** Add authentication to `/edit` routes. This can be done by modifying the example API routes in `/app/puck/api/route.ts` and server component in `/app/puck/[...puckPath]/page.tsx`. **If you don't do this, Puck will be completely public.**
56
- - Integrate your database into the API calls in `/app/puck/api/route.ts`
57
- - Implement a custom puck configuration in `puck.config.tsx`
58
- - Add business context for the AI generation in `/app/api/puck/[...all]/route.ts`
162
+ ## Learn more
59
163
 
60
- By default, this recipe will generate static pages by setting `dynamic` to [`force-static`](https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic) in the `/app/[...puckPath]/page.tsx`. This will strip headers and cookies. If you need dynamic pages, you can delete this.
164
+ - [Puck documentation](https://puckeditor.com/docs)
165
+ - [Getting started with Puck](https://puckeditor.com/docs/getting-started)
166
+ - [Integrating Puck](https://puckeditor.com/docs/integrating-puck/component-configuration)
167
+ - [Puck AI documentation](https://puckeditor.com/docs/ai/overview)
168
+ - [Getting started with Puck AI](https://puckeditor.com/docs/ai/getting-started)
169
+ - [Puck Discord](https://discord.gg/D9e4E3MQVZ)
@@ -2,8 +2,11 @@
2
2
 
3
3
  import type { Data } from "@puckeditor/core";
4
4
  import { Render } from "@puckeditor/core";
5
+ import { withDynamicConfig } from "@puckeditor/plugin-ai";
5
6
  import config from "../../puck.config";
6
7
 
7
8
  export function Client({ data }: { data: Data }) {
8
- return <Render config={config} data={data} />;
9
+ const configWithDesignedComponents = withDynamicConfig(config, data);
10
+
11
+ return <Render config={configWithDesignedComponents} data={data} />;
9
12
  }
@@ -1,12 +1,27 @@
1
1
  // Handles all requests for Puck AI
2
2
  // Learn more: https://puckeditor.com/docs/ai/getting-started
3
+ import type { NextRequest } from "next/server";
3
4
  import { puckHandler } from "@puckeditor/cloud-client";
4
5
 
5
- const handleRequest = (request) => {
6
+ const handleRequest = (request: NextRequest): Promise<Response> => {
6
7
  return puckHandler(request, {
7
8
  ai: {
8
9
  // Replace with your business context
9
10
  context: "We are Google. You create Google landing pages.",
11
+ designMode: {
12
+ // Allow AI to generate new components using "design mode"
13
+ // Learn more: https://puckeditor.com/docs/ai/design-mode
14
+ allowed: true,
15
+ // Constrain component generation, replace with your own instructions
16
+ instructions: `
17
+ #### Color Palette
18
+
19
+ Always use the following colors:
20
+
21
+ * Primary: \`#1976d2\`
22
+ * Secondary: \`#9c27b0\`
23
+ `,
24
+ },
10
25
  },
11
26
  });
12
27
  };
@@ -1,19 +1,32 @@
1
1
  "use client";
2
2
 
3
3
  import type { Data } from "@puckeditor/core";
4
- import { Puck } from "@puckeditor/core";
5
- import { createAiPlugin } from "@puckeditor/plugin-ai";
4
+ import { Puck, blocksPlugin, outlinePlugin } from "@puckeditor/core";
5
+ import { createAiPlugin, withDynamicConfig } from "@puckeditor/plugin-ai";
6
6
 
7
7
  import config from "../../../puck.config";
8
8
 
9
- const aiPlugin = createAiPlugin();
9
+ const aiPlugin = createAiPlugin({
10
+ // Allow users to switch between design and assembly mode.
11
+ // Read more: https://puckeditor.com/docs/ai/design-mode
12
+ designMode: {
13
+ visible: true,
14
+ },
15
+ // Select design mode by default.
16
+ defaultMode: "design",
17
+ });
18
+
19
+ // Place the ai plugin in the first position in the side nav.
20
+ const plugins = [aiPlugin, blocksPlugin(), outlinePlugin()];
10
21
 
11
22
  export function Client({ path, data }: { path: string; data: Partial<Data> }) {
23
+ const configWithDesignedComponents = withDynamicConfig(config, data);
24
+
12
25
  return (
13
26
  <Puck
14
- plugins={[aiPlugin]}
15
- config={config}
27
+ plugins={plugins}
16
28
  data={data}
29
+ config={configWithDesignedComponents}
17
30
  onPublish={async (data) => {
18
31
  await fetch("/api/pages", {
19
32
  method: "post",
@@ -1,35 +1,113 @@
1
- # `react-router` recipe
1
+ # Puck + React Router recipe
2
2
 
3
- The `react-router` recipe showcases one of the most powerful ways to implement Puck using to provide an authoring tool for any route in your React Router app.
3
+ [Puck](https://puckeditor.com) is the open-source visual editor for React.
4
4
 
5
- ## Demonstrates
5
+ This recipe connects Puck to [React Router](https://reactrouter.com) in framework mode, so you can create and edit pages for any route in this app.
6
6
 
7
- - React Router v7 (framework) implementation
8
- - JSON database implementation
9
- - Splat route to use puck for any route on the platform
7
+ ## Core concepts
10
8
 
11
- ## Usage
9
+ If you're new to Puck, this section introduces the core concepts you need to know.
12
10
 
13
- Run the generator and enter `react-router` when prompted
11
+ ### Puck
14
12
 
13
+ The Puck visual editor has three main parts: a config, the editor, and the renderer.
14
+
15
+ #### Config
16
+
17
+ The [config](https://puckeditor.com/docs/integrating-puck/component-configuration) registers the components users can use to build pages in the editor and the fields they can edit.
18
+
19
+ ```tsx
20
+ const config = {
21
+ components: {
22
+ HeadingBlock: {
23
+ fields: {
24
+ title: { type: "text" },
25
+ },
26
+ render: ({ title }) => <h1>{title}</h1>,
27
+ },
28
+ },
29
+ };
15
30
  ```
16
- npx create-puck-app my-app
31
+
32
+ #### The editor
33
+
34
+ The [`<Puck>`](https://puckeditor.com/docs/api-reference/components/puck) component renders the editor. It uses a config, exports [pages as JSON](https://puckeditor.com/docs/api-reference/data-model/data), and accepts initial page data for editing existing pages.
35
+
36
+ ```tsx
37
+ <Puck
38
+ config={config} // The components available to the editor
39
+ data={data} // The page JSON to edit
40
+ onPublish={(data) => {
41
+ // Save data to your database
42
+ }}
43
+ />
17
44
  ```
18
45
 
19
- Start the server
46
+ #### The renderer
20
47
 
48
+ The [`<Render>`](https://puckeditor.com/docs/api-reference/components/render) component renders pages. It expects the page JSON and the config used to create that page.
49
+
50
+ ```tsx
51
+ <Render
52
+ config={config} // The components used to create the page
53
+ data={data} // The page JSON to render
54
+ />
21
55
  ```
22
- yarn dev
56
+
57
+ ## Run the recipe
58
+
59
+ ### 1. Start the development server
60
+
61
+ Run:
62
+
63
+ ```sh
64
+ npm run dev
23
65
  ```
24
66
 
25
- Navigate to the homepage at http://localhost:5173/. To edit the homepage, access the Puck editor at http://localhost:5173/edit.
67
+ Once the server is running, navigate to [http://localhost:5173](http://localhost:5173) to view the home page, or [http://localhost:5173/edit](http://localhost:5173/edit) to edit it with Puck.
68
+
69
+ ### 2. Create a page
70
+
71
+ Navigate to [http://localhost:5173/edit](http://localhost:5173/edit), open the `Blocks` tab in the left sidebar and build your page by dragging components onto the canvas.
72
+
73
+ ### 3. Publish the page
74
+
75
+ Once your page is ready, select **Publish** in the header to save the result, then navigate to [http://localhost:5173](http://localhost:5173) to view the published page.
76
+
77
+ You can also create a page at any path by navigating to `/your/path/edit` and publishing it. The route `/your/path` will render the page.
78
+
79
+ ## How it works
80
+
81
+ When a URL ends in `/edit`, `resolvePuckPath` (`app/lib/resolve-puck-path.server.ts`) returns the path of the page being edited. The loader in `app/routes/puck-splat.tsx` loads the saved page, or starts with an empty page if the path is new.
82
+
83
+ Selecting **Publish** sends the page data to the action in `app/routes/puck-splat.tsx`. The action writes the JSON to `database.json`. The route then loads the same data and renders it with [`<Render>`](https://puckeditor.com/docs/api-reference/components/render).
84
+
85
+ The table below shows the files that implement this flow.
86
+
87
+ | File | Purpose |
88
+ | ------------------------------------- | --------------------------------------------------------------------------------------------------------- |
89
+ | `puck.config.tsx` | Defines the components, fields, and default props available to Puck. Add your own components here. |
90
+ | `app/routes.ts` | Registers the home page and catch-all page route. |
91
+ | `app/routes/puck-splat.tsx` | Loads and saves page data, then renders the editor or published page. |
92
+ | `app/routes/_index.tsx` | Loads and renders the home page. |
93
+ | `app/lib/resolve-puck-path.server.ts` | Maps an `/edit` URL to the path of the page being edited. |
94
+ | `app/lib/pages.server.ts` | Reads and writes page data in `database.json`. Replace this with your own data fetching and saving logic. |
95
+ | `app/components/puck-render.tsx` | Renders saved page data with `<Render>`. |
96
+ | `database.json` | Acts as a local database. Replace this with your own database solution. |
97
+
98
+ ## Before deploying to production
26
99
 
27
- You can do this for any **base** route on the application, **even if the page doesn't exist**. For example, visit http://localhost:5173/hello-world and you'll receive a 404. You can author and publish a page by visiting http://localhost:5173/hello-world/edit. After publishing, go back to the original URL to see your page.
100
+ Before deploying this recipe, make sure to:
28
101
 
29
- ## Using this recipe
102
+ - **Protect the editor and publishing.** The `/edit` routes and publish action are public by default. Add authentication and authorization so only trusted users can edit or publish pages.
103
+ - **Add your component library.** Replace the example `HeadingBlock` in `puck.config.tsx` with the components and fields your users need.
104
+ - **Use a real database.** Replace `database.json` and the functions in `app/lib/pages.server.ts`. Local files are not reliable across server instances or serverless deployments.
105
+ - **Choose a deployment strategy.** This recipe uses server-side rendering, loaders, and actions. Deploy it to a React Router-compatible server runtime.
30
106
 
31
- To adopt this recipe, you will need to:
107
+ ## Learn more
32
108
 
33
- - **IMPORTANT** Add authentication to `/edit` routes. This can be done by modifying the [route module action](https://reactrouter.com/start/framework/route-module#action) in the splat route `/app/routes/puck-splat.tsx`. **If you don't do this, Puck will be completely public.**
34
- - Integrate your database into the functions in `/lib/pages.server.ts`
35
- - Implement a custom puck configuration in `/app/puck.config.tsx`
109
+ - [Puck documentation](https://puckeditor.com/docs)
110
+ - [Getting started with Puck](https://puckeditor.com/docs/getting-started)
111
+ - [Integrating Puck](https://puckeditor.com/docs/integrating-puck/component-configuration)
112
+ - [React Router framework mode](https://reactrouter.com/start/framework/installation)
113
+ - [Puck Discord](https://discord.gg/D9e4E3MQVZ)
@@ -1,57 +1,172 @@
1
- # `react-router-ai` recipe
1
+ # Puck AI + React Router recipe
2
2
 
3
- The `react-router-ai` recipe showcases one of the most powerful ways to combine Puck and [Puck AI](https://puckeditor.com/docs/ai/overview): providing an authoring tool with AI page generation capabilities for any route in your React Router app.
3
+ [Puck](https://puckeditor.com) is the open-source visual editor for React. It lets you create page builders that use your own components.
4
4
 
5
- ## Demonstrates
5
+ [Puck AI](https://puckeditor.com/docs/ai/overview) builds on the same principles to let you generate pages by assembling your existing components or creating new ones on the fly, either as a copilot in the editor or headlessly.
6
6
 
7
- - Puck AI integration for generating pages with AI
8
- - React Router v7 (framework) implementation
9
- - JSON database implementation
10
- - Splat route to use puck for any route on the platform
7
+ This recipe connects Puck and Puck AI to [React Router](https://reactrouter.com) in framework mode, so you can create and edit pages for any route in this app.
11
8
 
12
- ## Usage
9
+ ## Core concepts
13
10
 
14
- Run the generator and select `React Router` when prompted
11
+ If you're new to Puck, this section introduces the core concepts you need to know.
15
12
 
16
- ```
17
- npx create-puck-app my-app
13
+ ### Puck
18
14
 
19
- ? Which recipe would you like to use?
20
- ❯ React Router
21
- ```
15
+ The Puck visual editor has three main parts: a config, the editor, and the renderer.
16
+
17
+ #### Config
22
18
 
23
- Confirm you want to use Puck AI
19
+ The [config](https://puckeditor.com/docs/integrating-puck/component-configuration) registers the components users can use to build pages in the editor and the fields they can edit.
24
20
 
21
+ ```tsx
22
+ const config = {
23
+ components: {
24
+ HeadingBlock: {
25
+ fields: {
26
+ title: { type: "text" },
27
+ },
28
+ render: ({ title }) => <h1>{title}</h1>,
29
+ },
30
+ },
31
+ };
25
32
  ```
26
- ? Would you like to use Puck AI? (Y/n) Y
33
+
34
+ #### The editor
35
+
36
+ The [`<Puck>`](https://puckeditor.com/docs/api-reference/components/puck) component renders the editor. It uses a config, exports [pages as JSON](https://puckeditor.com/docs/api-reference/data-model/data), and accepts initial page data for editing existing pages.
37
+
38
+ ```tsx
39
+ <Puck
40
+ config={config} // The components available to the editor
41
+ data={data} // The page JSON to edit
42
+ onPublish={(data) => {
43
+ // Save data to your database
44
+ }}
45
+ />
27
46
  ```
28
47
 
29
- Start the server
48
+ #### The renderer
49
+
50
+ The [`<Render>`](https://puckeditor.com/docs/api-reference/components/render) component renders pages. It expects the page JSON and the config used to create that page.
30
51
 
52
+ ```tsx
53
+ <Render
54
+ config={config} // The components used to create the page
55
+ data={data} // The page JSON to render
56
+ />
31
57
  ```
32
- cd my-app
33
- yarn dev
58
+
59
+ ### Puck AI
60
+
61
+ This recipe adds Puck AI as a copilot. It has two parts: the AI plugin (browser) and the Cloud Client (server).
62
+
63
+ #### The AI plugin
64
+
65
+ The [AI plugin](https://puckeditor.com/docs/api-reference/ai/ai-plugin/installation) renders the chat in the editor and sends each message to the Cloud Client on your server.
66
+
67
+ ```tsx
68
+ const aiPlugin = createAiPlugin();
69
+
70
+ function Editor() {
71
+ return <Puck plugins={[aiPlugin]} config={config} data={data} />;
72
+ }
34
73
  ```
35
74
 
36
- ### Set up Puck AI
75
+ #### The Cloud Client
37
76
 
38
- Create a [Puck account](https://cloud.puckeditor.com) and [obtain an API key](https://cloud.puckeditor.com/api-keys).
77
+ The [Cloud Client](https://puckeditor.com/docs/api-reference/ai/cloud-client/installation) provides APIs for connecting your server to the Puck cloud. This recipe uses its [`puckHandler`](https://puckeditor.com/docs/api-reference/ai/cloud-client/puck-handler) API, which receives each chat message, forwards it to the Puck cloud, and streams the response back to the plugin in the browser.
39
78
 
40
- Create a `.env.local` file in the root of your project and add your API key:
79
+ ```ts
80
+ const options = {
81
+ ai: {
82
+ context: "We are Google. You create Google landing pages.",
83
+ },
84
+ };
41
85
 
86
+ export function loader(args: LoaderFunctionArgs) {
87
+ return puckHandler(args.request, options);
88
+ }
89
+
90
+ export function action(args: ActionFunctionArgs) {
91
+ return puckHandler(args.request, options);
92
+ }
42
93
  ```
94
+
95
+ #### Puck AI modes
96
+
97
+ Puck AI can build pages in two ways:
98
+
99
+ - **Assembly mode** only builds pages using components from your config.
100
+ - **Design mode** can generate new components when needed.
101
+
102
+ This recipe comes with [Design mode](https://puckeditor.com/docs/api-reference/ai/cloud-client/puck-handler#aidesignmode) enabled out of the box.
103
+
104
+ ## Run the recipe
105
+
106
+ ### 1. Add a Puck API key
107
+
108
+ Start by creating an account, [generating an API key](https://cloud.puckeditor.com/api-keys), and adding it to an `.env.local` file:
109
+
110
+ ```sh
43
111
  PUCK_API_KEY=your-api-key
44
112
  ```
45
113
 
46
- Navigate to the homepage at https://localhost:3000. To edit the homepage, access the Puck editor at https://localhost:3000/edit, and select the AI button in the left navigation bar to generate content for the page using Puck AI.
114
+ ### 2. Start the development server
115
+
116
+ Run:
117
+
118
+ ```sh
119
+ npm run dev
120
+ ```
121
+
122
+ Once the server is running, navigate to [http://localhost:5173](http://localhost:5173) to view the home page, or [http://localhost:5173/edit](http://localhost:5173/edit) to edit it with Puck.
123
+
124
+ ### 3. Create a page with Puck AI
125
+
126
+ Navigate to [http://localhost:5173/edit](http://localhost:5173/edit), click the **AI** button in the left sidebar, enter a prompt, and press Enter.
127
+
128
+ ### 4. Publish the page
129
+
130
+ Once your page is ready, select **Publish** in the header to save the result, then navigate to [http://localhost:5173](http://localhost:5173) to view the published page.
131
+
132
+ You can also create a page at any path by navigating to `/your/path/edit` and publishing it. The route `/your/path` will render the page.
133
+
134
+ ## How it works
135
+
136
+ When a URL ends in `/edit`, `resolvePuckPath` (`app/lib/resolve-puck-path.server.ts`) returns the path of the page being edited. The loader in `app/routes/puck-splat.tsx` loads the saved page, or starts with an empty page if the path is new.
137
+
138
+ Selecting **Publish** sends the page data to the action in `app/routes/puck-splat.tsx`. The action writes the JSON to `database.json`. The route then loads the same data and renders it with [`<Render>`](https://puckeditor.com/docs/api-reference/components/render).
139
+
140
+ The table below shows the files that implement this flow.
141
+
142
+ | File | Purpose |
143
+ | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
144
+ | `puck.config.tsx` | Defines the components, fields, and default props available to Puck and Assembly mode. Add your own components here. |
145
+ | `app/routes.ts` | Registers the home page, Puck AI API, and catch-all page route. |
146
+ | `app/routes/puck-splat.tsx` | Loads and saves page data, then renders the editor or published page. |
147
+ | `app/routes/api.puck.ts` | Handles requests from the AI plugin and configures AI generation. |
148
+ | `app/routes/_index.tsx` | Loads and renders the home page. |
149
+ | `app/lib/resolve-puck-path.server.ts` | Maps an `/edit` URL to the path of the page being edited. |
150
+ | `app/lib/pages.server.ts` | Reads and writes page data in `database.json`. Replace this with your own data fetching and saving logic. |
151
+ | `app/components/puck-render.tsx` | Renders saved page data with `<Render>`. |
152
+ | `database.json` | Acts as a local database. Replace this with your own database solution. |
153
+
154
+ ## Before deploying to production
47
155
 
48
- You can do this for any route on the application, **even if the page doesn't exist**. For example, visit https://localhost:3000/hello/world and you'll receive a 404. You can author and publish a page by visiting https://localhost:3000/hello/world/edit. After publishing, go back to the original URL to see your page.
156
+ Before deploying this recipe, make sure to:
49
157
 
50
- ## Using this recipe
158
+ - **Protect the editor and APIs.** The `/edit` routes, publish action, and `/api/puck` route are public by default. Add authentication, authorization, and rate limits to protect page data and AI usage.
159
+ - **Add your component library.** Replace the example `HeadingBlock` in `puck.config.tsx` with the components and fields your users need.
160
+ - **Set your business context.** Replace the example Google context in `app/routes/api.puck.ts` with clear information about your product, audience, and content rules.
161
+ - **Use a real database.** Replace `database.json` and the functions in `app/lib/pages.server.ts`. Local files are not reliable across server instances or serverless deployments.
162
+ - **Choose a deployment strategy.** This recipe uses server-side rendering, loaders, and actions. Deploy it to a React Router-compatible server runtime.
51
163
 
52
- To adopt this recipe, you will need to:
164
+ ## Learn more
53
165
 
54
- - **IMPORTANT** Add authentication to `/edit` routes. This can be done by modifying the [route module action](https://reactrouter.com/start/framework/route-module#action) in the splat route `/app/routes/puck-splat.tsx`. **If you don't do this, Puck will be completely public.**
55
- - Integrate your database into the functions in `/lib/pages.server.ts`
56
- - Implement a custom puck configuration in `/app/puck.config.tsx`
57
- - Add business context for the AI generation in `/app/routes/api.puck.ts`
166
+ - [Puck documentation](https://puckeditor.com/docs)
167
+ - [Getting started with Puck](https://puckeditor.com/docs/getting-started)
168
+ - [Integrating Puck](https://puckeditor.com/docs/integrating-puck/component-configuration)
169
+ - [Puck AI documentation](https://puckeditor.com/docs/ai/overview)
170
+ - [Getting started with Puck AI](https://puckeditor.com/docs/ai/getting-started)
171
+ - [React Router framework mode](https://reactrouter.com/start/framework/installation)
172
+ - [Puck Discord](https://discord.gg/D9e4E3MQVZ)
@@ -1,8 +1,11 @@
1
1
  import type { Data } from "@puckeditor/core";
2
2
  import { Render } from "@puckeditor/core";
3
+ import { withDynamicConfig } from "@puckeditor/plugin-ai";
3
4
 
4
5
  import { config } from "../../puck.config";
5
6
 
6
7
  export function PuckRender({ data }: { data: Data }) {
7
- return <Render config={config} data={data} />;
8
+ const configWithDesignedComponents = withDynamicConfig(config, data);
9
+
10
+ return <Render config={configWithDesignedComponents} data={data} />;
8
11
  }
@@ -8,6 +8,20 @@ const options: PuckCloudOptions = {
8
8
  ai: {
9
9
  // Replace with your business context
10
10
  context: "We are Google. You create Google landing pages.",
11
+ designMode: {
12
+ // Allow AI to generate new components using "design mode"
13
+ // Learn more: https://puckeditor.com/docs/ai/design-mode
14
+ allowed: true,
15
+ // Constrain component generation, replace with your own instructions
16
+ instructions: `
17
+ #### Color Palette
18
+
19
+ Always use the following colors:
20
+
21
+ * Primary: \`#1976d2\`
22
+ * Secondary: \`#9c27b0\`
23
+ `,
24
+ },
11
25
  },
12
26
  };
13
27
 
@@ -1,12 +1,15 @@
1
+ import { useMemo } from "react";
1
2
  import { useFetcher, useLoaderData } from "react-router";
2
3
  import type { Data } from "@puckeditor/core";
3
- import { Puck, Render } from "@puckeditor/core";
4
- import { createAiPlugin } from "@puckeditor/plugin-ai";
4
+ import { Puck, blocksPlugin, outlinePlugin } from "@puckeditor/core";
5
+ import { createAiPlugin, withDynamicConfig } from "@puckeditor/plugin-ai";
5
6
 
6
7
  import type { Route } from "./+types/puck-splat";
7
8
  import { config } from "../../puck.config";
8
9
  import { resolvePuckPath } from "~/lib/resolve-puck-path.server";
9
10
  import { getPage, savePage } from "~/lib/pages.server";
11
+ import { PuckRender } from "~/components/puck-render";
12
+
10
13
  import editorStyles from "@puckeditor/core/puck.css?url";
11
14
  import pluginStyles from "@puckeditor/plugin-ai/styles.css?url";
12
15
 
@@ -57,19 +60,35 @@ export async function action({ params, request }: Route.ActionArgs) {
57
60
  await savePage(path, body.data);
58
61
  }
59
62
 
60
- const aiPlugin = createAiPlugin();
63
+ const aiPlugin = createAiPlugin({
64
+ // Allow users to switch between design and assembly mode.
65
+ // Read more: https://puckeditor.com/docs/ai/design-mode
66
+ designMode: {
67
+ visible: true,
68
+ },
69
+ // Select design mode by default.
70
+ defaultMode: "design",
71
+ });
72
+
73
+ // Place the ai plugin in the first position in the side nav.
74
+ const plugins = [aiPlugin, blocksPlugin(), outlinePlugin()];
61
75
 
62
76
  function Editor() {
63
77
  const loaderData = useLoaderData<typeof loader>();
64
78
  const fetcher = useFetcher<typeof action>();
65
79
 
80
+ const configWithDesignedComponents = useMemo(
81
+ () => withDynamicConfig(config, loaderData.data),
82
+ [config, loaderData.data]
83
+ );
84
+
66
85
  return (
67
86
  <>
68
87
  <link rel="stylesheet" href={editorStyles} id="puck-css" />
69
88
  <link rel="stylesheet" href={pluginStyles} id="puck-plugin-ai-css" />
70
89
  <Puck
71
- plugins={[aiPlugin]}
72
- config={config}
90
+ plugins={plugins}
91
+ config={configWithDesignedComponents}
73
92
  data={loaderData.data}
74
93
  onPublish={async (data) => {
75
94
  await fetcher.submit(
@@ -94,7 +113,7 @@ export default function PuckSplatRoute({ loaderData }: Route.ComponentProps) {
94
113
  {loaderData.isEditorRoute ? (
95
114
  <Editor />
96
115
  ) : (
97
- <Render config={config} data={loaderData.data} />
116
+ <PuckRender data={loaderData.data} />
98
117
  )}
99
118
  </div>
100
119
  );