@picked/client 0.0.64-next.3 → 0.0.64-next.5
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/dist/{api-Dktr48v4.cjs → api-CqlAb5uM.cjs} +1 -1
- package/dist/{api-DF8BSFOG.js → api-Z6vbOYDq.js} +1 -1
- package/dist/cjs/form/index.js +1 -1
- package/dist/cjs/index.js +2 -2
- package/dist/cjs/plugins/create-experience/index.js +1 -1
- package/dist/cjs/plugins/dom-observer/index.js +1 -1
- package/dist/cjs/plugins/dynamic-experiences/index.js +1 -1
- package/dist/cjs/plugins/experience-browser-storage/index.js +1 -1
- package/dist/cjs/plugins/history-browser-storage/index.js +1 -1
- package/dist/cjs/plugins/responses-browser-storage/index.js +1 -1
- package/dist/cjs/plugins/responses-indexeddb/index.js +1 -1
- package/dist/cjs/plugins/single-page-app/index.js +1 -1
- package/dist/cjs/plugins/web-components/index.js +1 -1
- package/dist/cjs/plugins/window-object/index.js +1 -1
- package/dist/cjs/server/index.js +1 -1
- package/dist/docs/api/index.md +4 -0
- package/dist/docs/content/asset-library.mdx +50 -0
- package/dist/docs/content/content-entries.mdx +30 -0
- package/dist/docs/content/layouts.mdx +70 -0
- package/dist/docs/content/pages.mdx +67 -0
- package/dist/docs/content/sections.mdx +33 -0
- package/dist/docs/delivery-api/experience-content.md +85 -0
- package/dist/docs/delivery-api/index.md +46 -0
- package/dist/docs/delivery-api/preview-caching-crawling.md +59 -0
- package/dist/docs/delivery-api/project-config.md +56 -0
- package/dist/docs/experiences.mdx +3 -1
- package/dist/docs/fields.mdx +2 -0
- package/dist/docs/guides/build-a-personalized-page.mdx +143 -0
- package/dist/docs/sdk/experiences.md +2 -0
- package/dist/docs/sdk/index.md +11 -1
- package/dist/docs/sdk/ssr/index.md +62 -0
- package/dist/docs/sdk/ssr/rendering-and-hydration.md +141 -0
- package/dist/docs/sdk/ssr/serialize.md +86 -0
- package/dist/docs/sdk/ssr/slots-and-placements.md +35 -0
- package/dist/docs/steps.mdx +2 -0
- package/dist/{dom-DjfSrkpp.js → dom-BwmTHeT7.js} +1 -1
- package/dist/{dom-sqnnO-CT.cjs → dom-CDrHc_j9.cjs} +1 -1
- package/dist/esm/form/index.js +1 -1
- package/dist/esm/index.js +10 -10
- package/dist/esm/plugins/create-experience/index.js +1 -1
- package/dist/esm/plugins/dom-observer/index.js +3 -3
- package/dist/esm/plugins/dynamic-experiences/index.js +2 -2
- package/dist/esm/plugins/experience-browser-storage/index.js +3 -3
- package/dist/esm/plugins/history-browser-storage/index.js +2 -2
- package/dist/esm/plugins/responses-browser-storage/index.js +2 -2
- package/dist/esm/plugins/responses-indexeddb/index.js +1 -1
- package/dist/esm/plugins/single-page-app/index.js +2 -2
- package/dist/esm/plugins/web-components/index.js +1 -1
- package/dist/esm/plugins/window-object/index.js +1 -1
- package/dist/esm/server/index.js +2 -2
- package/dist/{experience-instance-BT_H2Zln.cjs → experience-instance-CGzLsO7F.cjs} +1 -1
- package/dist/{experience-instance-v635I4oP.js → experience-instance-RJiegFtc.js} +3 -3
- package/dist/id-Js-62re-.cjs +1 -0
- package/dist/logger-CSTvAyyB.cjs +1 -0
- package/dist/{object-ThO1DEyl.cjs → object-CIkt7Fkr.cjs} +1 -1
- package/dist/{plugin-BQiuKtPl.js → plugin-D9YNJv5f.js} +2 -2
- package/dist/{plugin-DbiThXW5.cjs → plugin-DLFhcssK.cjs} +1 -1
- package/dist/{preview-bridge-DIdDvNXQ.cjs → preview-bridge-CpX1BkSA.cjs} +1 -1
- package/dist/{preview-bridge-CGyx7dEx.js → preview-bridge-D76l-dPy.js} +2 -2
- package/dist/rules-logic-CGZIYgAI.cjs +1 -0
- package/dist/{rules-logic-D_CVBuv7.js → rules-logic-HesIUCu6.js} +1 -1
- package/dist/{storage-BXSlLTRa.cjs → storage-DUw9UJ2h.cjs} +1 -1
- package/dist/{supports-34OzZK5j.cjs → supports-DBzjGaWD.cjs} +1 -1
- package/package.json +1 -1
- package/dist/id-Cg7fVl1O.cjs +0 -1
- package/dist/logger-fbvQC40v.cjs +0 -1
- package/dist/rules-logic-H6P8xwHF.cjs +0 -1
- /package/dist/{id-n2yrHvNo.js → id-Bo4RBeOs.js} +0 -0
- /package/dist/{logger-6z_gb4rA.js → logger-DMN6TxJJ.js} +0 -0
- /package/dist/{object-DvUDOytC.js → object-BUDGxEqy.js} +0 -0
- /package/dist/{storage-hOHCCXvP.js → storage-1MW3-_As.js} +0 -0
- /package/dist/{supports-BgjwyijI.js → supports-CO9bJGNl.js} +0 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: docs
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
import { Screenshot } from '../../../components/Screenshot'
|
|
6
|
+
|
|
7
|
+
# Pages
|
|
8
|
+
|
|
9
|
+
A Page is a standalone, server-rendered experience backed by CMS content, built for SEO-critical landing pages.
|
|
10
|
+
|
|
11
|
+
<Screenshot
|
|
12
|
+
route="/{orgSlug}/{projectId}/workspaces/{workspaceId}/experiences"
|
|
13
|
+
alt="The experiences list, where Pages and Flows are created on the Picked dashboard"
|
|
14
|
+
/>
|
|
15
|
+
|
|
16
|
+
## Pages vs flows
|
|
17
|
+
|
|
18
|
+
Picked has two kinds of experience:
|
|
19
|
+
|
|
20
|
+
| Experience | What it is | Use it for |
|
|
21
|
+
| --- | --- | --- |
|
|
22
|
+
| **Page** | A standalone experience rendered on your server, backed by a Content Entry. | SEO-critical landing pages, campaign pages, content the crawler must see. |
|
|
23
|
+
| **Flow** | A multi-step, interactive experience rendered in the browser. | Quizzes, surveys, product finders, multi-step onboarding. |
|
|
24
|
+
|
|
25
|
+
Reach for a **Page** when the content must appear in the server HTML for search engines and social unfurls. Reach for a **Flow** when the value is in the interaction, and client-side rendering is fine. See [Experiences](/experiences) for the wider model.
|
|
26
|
+
|
|
27
|
+
## How a page fits together
|
|
28
|
+
|
|
29
|
+
Four pieces back every Page:
|
|
30
|
+
|
|
31
|
+
1. A **Page Layout** defines the shape: the fields a page can hold. See [Page & Step Layouts](/content/layouts).
|
|
32
|
+
2. A **Content Entry** fills that shape with real data. See [Content Entries](/content/content-entries).
|
|
33
|
+
3. A **Page** binds one entry so the SDK can render it.
|
|
34
|
+
4. The **SDK** fetches the entry and renders it on your server. See [Server-Side Rendering](/sdk/ssr).
|
|
35
|
+
|
|
36
|
+
A layout defines the shape, a content entry fills it with data, a Page binds an entry so the SDK can render it. Step Layouts do the same for individual Steps inside a Flow.
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
Author (dashboard) -> Publish -> Delivery API (config + content)
|
|
40
|
+
-> serialize() on server -> Provider seeds client -> ExperienceSlot renders -> hydrate
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Create a page
|
|
44
|
+
|
|
45
|
+
1. Open your project and select a workspace.
|
|
46
|
+
2. Click **Experiences** in the sidebar.
|
|
47
|
+
3. Click **New Experience** and choose the **Page** type.
|
|
48
|
+
4. Pick a **Page Layout**. The layout determines which fields the page holds.
|
|
49
|
+
5. Click **Create**.
|
|
50
|
+
|
|
51
|
+
A Content Entry is created and linked to the page on its first save, so the page always has somewhere to store its data.
|
|
52
|
+
|
|
53
|
+
## Edit page content
|
|
54
|
+
|
|
55
|
+
The Page editor has a **Properties** tab for the page's content and a **Settings** tab for its name, condition, and layout. Fill in the fields on the **Properties** tab. The inspector on the right edits the selected field, and the preview panel shows the page as it fills in.
|
|
56
|
+
|
|
57
|
+
## Change or remove the layout
|
|
58
|
+
|
|
59
|
+
Use the change-layout control on the **Settings** tab to switch the page to a different Page Layout. Unlinking a layout detaches the Content Entry from the page. The entry is not deleted, but the page no longer has content to render until you bind a layout again.
|
|
60
|
+
|
|
61
|
+
## Validate and publish
|
|
62
|
+
|
|
63
|
+
Invalid fields surface inline as per-field errors while you edit, so you can fix them before publishing. When the page is valid, publish the workspace to make it live. Publishing produces the version the Delivery API serves to anonymous readers.
|
|
64
|
+
|
|
65
|
+
## Render a page
|
|
66
|
+
|
|
67
|
+
A published Page is rendered by the SDK on your server. See the [Build a Personalized Page](/guides/build-a-personalized-page) guide for the end-to-end walkthrough, and [Server-Side Rendering](/sdk/ssr) for the SDK reference.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: docs
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
import { Screenshot } from '../../../components/Screenshot'
|
|
6
|
+
|
|
7
|
+
# Sections
|
|
8
|
+
|
|
9
|
+
Sections are reusable schema fragments you embed inside layouts, so common structures such as a hero or a feature list are defined once.
|
|
10
|
+
|
|
11
|
+
<Screenshot
|
|
12
|
+
route="/{orgSlug}/{projectId}/workspaces/{workspaceId}/blocks"
|
|
13
|
+
alt="The sections list in the schema builder on the Picked dashboard"
|
|
14
|
+
/>
|
|
15
|
+
|
|
16
|
+
## What a section is
|
|
17
|
+
|
|
18
|
+
A Section is a reusable fragment of schema with its own **Key**. Where a [layout](/content/layouts) backs a whole Page or Step, a Section is a piece you compose into one or more layouts. Define the shape once, reuse it everywhere.
|
|
19
|
+
|
|
20
|
+
## Create a section
|
|
21
|
+
|
|
22
|
+
1. Open your workspace and click **Sections** in the sidebar.
|
|
23
|
+
2. Click **New Section**.
|
|
24
|
+
3. Enter a name and a **Key**. The Key is a unique, stable identifier used in SDK code and API references.
|
|
25
|
+
4. Add fields and save.
|
|
26
|
+
|
|
27
|
+
## Use a section in a layout
|
|
28
|
+
|
|
29
|
+
Add a **Section** field to a Page or Step Layout and point it at the Section you want to embed. The layout then carries the Section's fields wherever that field appears. A Content Entry built from the layout fills the Section's fields like any others.
|
|
30
|
+
|
|
31
|
+
## Changing a section
|
|
32
|
+
|
|
33
|
+
Editing a Section does not automatically migrate the entries that already use it. After changing a Section, revalidate the affected [Content Entries](/content/content-entries) and resolve any per-field errors before publishing.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: docs
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Experience Content Endpoint
|
|
6
|
+
|
|
7
|
+
Fetch the Content Entries that back a single experience's steps.
|
|
8
|
+
|
|
9
|
+
## Request
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
GET /api/v1/projects/:projectId/experiences/:id/content
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
| Parameter | In | Description |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| `projectId` | path | The project the experience belongs to. |
|
|
18
|
+
| `id` | path | The experience to fetch content for. |
|
|
19
|
+
| `preview_token` | query | Optional. Returns preview (unpublished) content when present. |
|
|
20
|
+
|
|
21
|
+
## Response
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"experienceId": "exp_123",
|
|
26
|
+
"versionId": "ver_456",
|
|
27
|
+
"content": [
|
|
28
|
+
{
|
|
29
|
+
"id": "entry_789",
|
|
30
|
+
"contentModelKey": "page-hero",
|
|
31
|
+
"data": { "title": "Spring sale", "body": "..." }
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
| Field | Type | Description |
|
|
38
|
+
| --- | --- | --- |
|
|
39
|
+
| `experienceId` | `string` | The experience the content belongs to. |
|
|
40
|
+
| `versionId` | `string \| null` | The published version id, or `null` on preview. |
|
|
41
|
+
| `content[].id` | `string` | The Content Entry id. |
|
|
42
|
+
| `content[].contentModelKey` | `string \| null` | The layout's Key, or `null` when it cannot be resolved. |
|
|
43
|
+
| `content[].data` | `object` | The entry data, matching the layout schema. |
|
|
44
|
+
|
|
45
|
+
## HTML asset inlining
|
|
46
|
+
|
|
47
|
+
When the layout has an [HTML asset](/content/asset-library) field, its value arrives inlined. The reference is rewritten to `{ id, content }`, where `content` is the uploaded HTML as a string (or `null` when it cannot be resolved):
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"id": "entry_789",
|
|
52
|
+
"contentModelKey": "page-hero",
|
|
53
|
+
"data": {
|
|
54
|
+
"markup": { "id": "asset_42", "content": "<section>...</section>" }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The SDK renders the inlined HTML directly, with no extra storage fetch.
|
|
60
|
+
|
|
61
|
+
## Status codes
|
|
62
|
+
|
|
63
|
+
| Code | Meaning |
|
|
64
|
+
| --- | --- |
|
|
65
|
+
| `200` | Success. |
|
|
66
|
+
| `401` | Invalid `preview_token`. |
|
|
67
|
+
| `404` | The experience has no published content. |
|
|
68
|
+
|
|
69
|
+
## Example
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
curl https://api.picked.app/api/v1/projects/proj_123/experiences/exp_123/content
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"experienceId": "exp_123",
|
|
78
|
+
"versionId": "ver_456",
|
|
79
|
+
"content": [
|
|
80
|
+
{ "id": "entry_789", "contentModelKey": "page-hero", "data": { "title": "Spring sale" } }
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
See [Project Config](/delivery-api/project-config) for the endpoint that links steps to entries, [Asset Library](/content/asset-library) for HTML assets, and [Server Snapshot](/sdk/ssr/serialize) for how the SDK consumes this content.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: docs
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Delivery API
|
|
6
|
+
|
|
7
|
+
The Delivery API is a public, read-only REST API that serves your published project config and page content. It is separate from the GraphQL management API.
|
|
8
|
+
|
|
9
|
+
## Delivery API vs the management API
|
|
10
|
+
|
|
11
|
+
Picked has two HTTP APIs. Use the one that fits the job:
|
|
12
|
+
|
|
13
|
+
| | Delivery API | Management API |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| Style | REST | GraphQL |
|
|
16
|
+
| Access | Public, anonymous | Authenticated |
|
|
17
|
+
| Operations | Read-only | Read and write |
|
|
18
|
+
| Use for | Serving published config and content to your app | Managing projects, experiences, and content |
|
|
19
|
+
|
|
20
|
+
The management API is documented under [API Reference](/api). This section covers the Delivery API.
|
|
21
|
+
|
|
22
|
+
## Base URL and versioning
|
|
23
|
+
|
|
24
|
+
All Delivery API routes are versioned under `/api/v1`:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
https://api.picked.app/api/v1
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Published vs preview
|
|
31
|
+
|
|
32
|
+
Each endpoint serves two surfaces:
|
|
33
|
+
|
|
34
|
+
- **Published** content is anonymous. Anyone can read your published config and content.
|
|
35
|
+
- **Preview** content is gated by a `preview_token` query parameter, so you can render unpublished drafts before going live.
|
|
36
|
+
|
|
37
|
+
A workspace's preview token is available as the `previewToken` field on the workspace in the management API, and the dashboard preview action issues one for you.
|
|
38
|
+
|
|
39
|
+
## Endpoints
|
|
40
|
+
|
|
41
|
+
| Endpoint | Description |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| [Project Config](/delivery-api/project-config) | `GET /api/v1/projects/:projectId` returns the published project config, including each step's content link. |
|
|
44
|
+
| [Experience Content](/delivery-api/experience-content) | `GET /api/v1/projects/:projectId/experiences/:id/content` returns the Content Entries that back an experience. |
|
|
45
|
+
|
|
46
|
+
For the operational details shared by both endpoints, see [Preview, Caching & Crawling](/delivery-api/preview-caching-crawling).
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: docs
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Preview, Caching & Crawling
|
|
6
|
+
|
|
7
|
+
The operational surface shared by both Delivery API endpoints: preview tokens, caching, CORS, and crawling.
|
|
8
|
+
|
|
9
|
+
## Preview tokens
|
|
10
|
+
|
|
11
|
+
Pass `?preview_token=<token>` to read unpublished content. Both endpoints accept it, and both return `versionId: null` on the preview surface. An invalid token returns `401`.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
curl "https://api.picked.app/api/v1/projects/proj_123?preview_token=<token>"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
A workspace's preview token is available as the `previewToken` field on the workspace in the [management API](/api), and the dashboard preview action issues one for you. Use the same token for the project config and the experience content requests so both come from the same preview surface.
|
|
18
|
+
|
|
19
|
+
## Caching
|
|
20
|
+
|
|
21
|
+
Caching depends on the surface and the requesting client. The SDK sends a `Picked-Client: sdk` request header:
|
|
22
|
+
|
|
23
|
+
| Request | `Cache-Control` |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| Published, `Picked-Client: sdk` | `public, s-maxage=60, stale-while-revalidate=86400` |
|
|
26
|
+
| Published, other clients | `private, no-store` |
|
|
27
|
+
| Preview | `no-store` |
|
|
28
|
+
|
|
29
|
+
Cacheable published responses also send `Vary: Picked-Client`, so a shared cache keys on the client header and never serves an SDK-cached body to a non-SDK request.
|
|
30
|
+
|
|
31
|
+
## CORS
|
|
32
|
+
|
|
33
|
+
Both endpoints are cross-origin readable. An `OPTIONS` preflight returns `204` with the CORS headers:
|
|
34
|
+
|
|
35
|
+
| Header | Value |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| `Access-Control-Allow-Origin` | `*` |
|
|
38
|
+
| `Access-Control-Allow-Methods` | `GET, OPTIONS` |
|
|
39
|
+
| `Access-Control-Allow-Headers` | `Content-Type, Picked-Client, Picked-Version, traceparent, tracestate` |
|
|
40
|
+
|
|
41
|
+
## Crawling and SEO
|
|
42
|
+
|
|
43
|
+
Both endpoints are crawlable so search engines can read your published content. Preview URLs are disallowed, so drafts never reach the index. The `robots.txt` rules:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
User-agent: *
|
|
47
|
+
|
|
48
|
+
# Allow crawling of the public project config and experience content endpoints
|
|
49
|
+
Allow: /api/v1/projects
|
|
50
|
+
Allow: /api/v1/projects/*/experiences/*/content
|
|
51
|
+
|
|
52
|
+
# Keep preview (draft) responses out of crawlers
|
|
53
|
+
Disallow: /api/v1/projects/*preview_token=
|
|
54
|
+
|
|
55
|
+
# Everything else (GraphQL, MCP, internal, ping) stays disallowed
|
|
56
|
+
Disallow: /
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
See [Project Config](/delivery-api/project-config) and [Experience Content](/delivery-api/experience-content) for the endpoints themselves.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: docs
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Project Config Endpoint
|
|
6
|
+
|
|
7
|
+
Fetch a project's published configuration: its experiences, steps, and the link from each step to the Content Entry that backs it.
|
|
8
|
+
|
|
9
|
+
## Request
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
GET /api/v1/projects/:projectId
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
| Parameter | In | Description |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| `projectId` | path | The project to fetch config for. |
|
|
18
|
+
| `preview_token` | query | Optional. Returns the preview (unpublished) config when present. |
|
|
19
|
+
|
|
20
|
+
## Response
|
|
21
|
+
|
|
22
|
+
The response carries the project's experiences and steps. Each step includes `layoutContentId`, the reference to the Content Entry that backs that step's layout. Use it to know which experiences have content to fetch from the [Experience Content](/delivery-api/experience-content) endpoint.
|
|
23
|
+
|
|
24
|
+
| Field | Type | Description |
|
|
25
|
+
| --- | --- | --- |
|
|
26
|
+
| `id` | `string` | The project id. |
|
|
27
|
+
| `versionId` | `string \| null` | The published version id, or `null` on preview. |
|
|
28
|
+
| `steps[].layoutContentId` | `string \| null` | The Content Entry backing this step, or `null` when the step has no content. |
|
|
29
|
+
|
|
30
|
+
## Published vs preview
|
|
31
|
+
|
|
32
|
+
On the published surface, `versionId` is the published version id. On the preview surface (`?preview_token=...`), `versionId` is `null` because there is no published version. See [Preview, Caching & Crawling](/delivery-api/preview-caching-crawling).
|
|
33
|
+
|
|
34
|
+
## Example
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
curl https://api.picked.app/api/v1/projects/proj_123
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"id": "proj_123",
|
|
43
|
+
"versionId": "ver_456",
|
|
44
|
+
"steps": [
|
|
45
|
+
{
|
|
46
|
+
"id": "step_1",
|
|
47
|
+
"key": "hero",
|
|
48
|
+
"name": "Hero",
|
|
49
|
+
"layoutName": "HeroLayout",
|
|
50
|
+
"layoutContentId": "entry_789"
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Pass the config to `serialize()` on your server to produce an SSR snapshot. See [Server Snapshot](/sdk/ssr/serialize).
|
|
@@ -6,7 +6,9 @@ import { Screenshot } from '../../components/Screenshot'
|
|
|
6
6
|
|
|
7
7
|
# Experiences
|
|
8
8
|
|
|
9
|
-
An experience is a personalized
|
|
9
|
+
An experience is a personalized experience delivered to visitors on your site. Each experience maps to a component registered in your application.
|
|
10
|
+
|
|
11
|
+
Picked has two kinds of experience. A **Flow** is a multi-step, interactive experience rendered in the browser, covered on this page. A **Page** is a standalone, server-rendered experience backed by CMS content, built for SEO-critical landing pages. See [Pages](/content/pages) for that kind.
|
|
10
12
|
|
|
11
13
|
<Screenshot
|
|
12
14
|
route="/{orgSlug}/{projectId}/workspaces/{workspaceId}/experiences"
|
package/dist/docs/fields.mdx
CHANGED
|
@@ -8,6 +8,8 @@ import { Screenshot } from '../../components/Screenshot'
|
|
|
8
8
|
|
|
9
9
|
A field is a data input shown to visitors inside a step. Fields collect information like names, email addresses, preferences, or selections.
|
|
10
10
|
|
|
11
|
+
These are experience Fields, which collect visitor input. They are distinct from the Content Fields that make up a CMS layout's schema, covered in [Page & Step Layouts](/content/layouts).
|
|
12
|
+
|
|
11
13
|
<Screenshot
|
|
12
14
|
route="/{orgSlug}/{projectId}/workspaces/{workspaceId}/fields"
|
|
13
15
|
alt="The fields list for a workspace on the Picked dashboard"
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: docs
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
import { Screenshot } from '../../../components/Screenshot'
|
|
6
|
+
|
|
7
|
+
# Build a Personalized Page
|
|
8
|
+
|
|
9
|
+
Build a personalized landing page with Picked: model it in the dashboard, publish it, and render it server-side in your app.
|
|
10
|
+
|
|
11
|
+
## What you will build
|
|
12
|
+
|
|
13
|
+
A standalone, SEO-critical landing page whose content lives in Picked's CMS and renders on your server. You model the page's shape once, fill it with content, publish it, then fetch and render it in React or Svelte. The lifecycle from authoring to a hydrated page:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Author (dashboard) -> Publish -> Delivery API (config + content)
|
|
17
|
+
-> serialize() on server -> Provider seeds client -> ExperienceSlot renders -> hydrate
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Model a page layout
|
|
21
|
+
|
|
22
|
+
A [Page Layout](/content/layouts) defines the fields your page can hold.
|
|
23
|
+
|
|
24
|
+
1. Open your workspace and click **Models** in the sidebar.
|
|
25
|
+
2. Click **New Model** and choose **Page Layout**.
|
|
26
|
+
3. Enter a name and a **Key**, for example `page-hero`. The Key is a unique, stable identifier you use in SDK code and API references.
|
|
27
|
+
4. Add a few fields: a **Single-line text** field `title`, a **Markdown** field `body`, and an **Asset** field `image`.
|
|
28
|
+
5. Save the layout.
|
|
29
|
+
|
|
30
|
+
<Screenshot
|
|
31
|
+
route="/{orgSlug}/{projectId}/workspaces/{workspaceId}/models"
|
|
32
|
+
alt="A page layout in the schema builder on the Picked dashboard"
|
|
33
|
+
/>
|
|
34
|
+
|
|
35
|
+
See [Page & Step Layouts](/content/layouts) for every field type.
|
|
36
|
+
|
|
37
|
+
## Create and fill the page
|
|
38
|
+
|
|
39
|
+
1. Click **Experiences** in the sidebar, then **New Experience**.
|
|
40
|
+
2. Choose the **Page** type and pick your `page-hero` layout.
|
|
41
|
+
3. On the **Properties** tab, fill in `title`, `body`, and `image`. A [Content Entry](/content/content-entries) is created and linked on first save.
|
|
42
|
+
4. Fix any per-field validation errors.
|
|
43
|
+
5. Publish the workspace to make the page live.
|
|
44
|
+
|
|
45
|
+
See [Pages](/content/pages) for the full editor reference.
|
|
46
|
+
|
|
47
|
+
## Fetch the config and content
|
|
48
|
+
|
|
49
|
+
Your server fetches two things from the [Delivery API](/delivery-api): the project config (which experiences and steps exist) and the content for the matching experience.
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# 1. Project config: find the steps and their content links
|
|
53
|
+
curl https://api.picked.app/api/v1/projects/proj_123
|
|
54
|
+
|
|
55
|
+
# 2. Experience content: the entry data backing the page
|
|
56
|
+
curl https://api.picked.app/api/v1/projects/proj_123/experiences/exp_123/content
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
In practice you fetch the config, then let `serialize()` fetch the content for you. See [Project Config](/delivery-api/project-config) and [Experience Content](/delivery-api/experience-content).
|
|
60
|
+
|
|
61
|
+
## Render server-side and hydrate
|
|
62
|
+
|
|
63
|
+
On the server, create a per-request client, call `serialize()` to produce a snapshot, render your app, and embed the snapshot. On the client, seed from the snapshot and hydrate.
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
// server.ts
|
|
67
|
+
import { createClient } from '@picked/client'
|
|
68
|
+
import { serialize } from '@picked/client/server'
|
|
69
|
+
|
|
70
|
+
const client = createClient(options)
|
|
71
|
+
const snapshot = await serialize(client, {
|
|
72
|
+
projectConfig,
|
|
73
|
+
attributes: { location: { pathname: url.pathname } },
|
|
74
|
+
})
|
|
75
|
+
// Render your app, then embed `snapshot` in the HTML response.
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Render `<ExperienceSlot name="page" />` inside the provider, and register the component the Page maps to (here, `CMSPage`):
|
|
79
|
+
|
|
80
|
+
:::code-group
|
|
81
|
+
|
|
82
|
+
```tsx [React]
|
|
83
|
+
import type { CreateClientOptions } from '@picked/client'
|
|
84
|
+
import { dynamicExperiencesPlugin } from '@picked/client/plugins/dynamic-experiences'
|
|
85
|
+
import { adaptReactComponent, ExperienceSlot, PickedProvider } from '@picked/react'
|
|
86
|
+
import { reactFrameworkPlugin } from '@picked/react/plugins/framework'
|
|
87
|
+
import { useStepContent } from '@picked/react/hooks'
|
|
88
|
+
|
|
89
|
+
function CMSPage() {
|
|
90
|
+
const content = useStepContent()
|
|
91
|
+
const title = typeof content?.title === 'string' ? content.title : ''
|
|
92
|
+
return <h1>{title}</h1>
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const options = {
|
|
96
|
+
projectId: 'proj_123',
|
|
97
|
+
components: { CMSPage: adaptReactComponent(CMSPage) },
|
|
98
|
+
plugins: [reactFrameworkPlugin({ components: {} }), dynamicExperiencesPlugin()],
|
|
99
|
+
} satisfies CreateClientOptions
|
|
100
|
+
|
|
101
|
+
export function Page({ snapshot }) {
|
|
102
|
+
return (
|
|
103
|
+
<PickedProvider options={options} snapshot={snapshot}>
|
|
104
|
+
<ExperienceSlot name="page" />
|
|
105
|
+
</PickedProvider>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
```svelte [Svelte]
|
|
111
|
+
<script lang="ts">
|
|
112
|
+
import type { CreateClientOptions } from '@picked/client'
|
|
113
|
+
import { dynamicExperiencesPlugin } from '@picked/client/plugins/dynamic-experiences'
|
|
114
|
+
import { adaptSvelteComponent, ExperienceSlot, setPickedContext } from '@picked/svelte'
|
|
115
|
+
import { svelteFrameworkPlugin } from '@picked/svelte/plugins/framework'
|
|
116
|
+
import CMSPage from './CMSPage.svelte'
|
|
117
|
+
|
|
118
|
+
const { snapshot } = $props()
|
|
119
|
+
|
|
120
|
+
const options = {
|
|
121
|
+
projectId: 'proj_123',
|
|
122
|
+
components: { CMSPage: adaptSvelteComponent(CMSPage) },
|
|
123
|
+
plugins: [svelteFrameworkPlugin({ components: {} }), dynamicExperiencesPlugin()],
|
|
124
|
+
} satisfies CreateClientOptions
|
|
125
|
+
|
|
126
|
+
setPickedContext({ options, snapshot })
|
|
127
|
+
</script>
|
|
128
|
+
|
|
129
|
+
<ExperienceSlot name="page" />
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
:::
|
|
133
|
+
|
|
134
|
+
The provider seeds synchronously, so the server HTML and the client's first paint match, then the SDK hydrates. See [Server-Side Rendering](/sdk/ssr) for the full reference.
|
|
135
|
+
|
|
136
|
+
## Verify SEO
|
|
137
|
+
|
|
138
|
+
Confirm the page is crawlable end to end:
|
|
139
|
+
|
|
140
|
+
1. View the server response (for example with `curl` or your browser's view-source) and check the rendered HTML contains your `title` and `body`, not an empty shell.
|
|
141
|
+
2. Confirm the content endpoint is reachable anonymously and returns your entry data.
|
|
142
|
+
|
|
143
|
+
Published Delivery API responses are crawlable; preview URLs are disallowed. See [Preview, Caching & Crawling](/delivery-api/preview-caching-crawling).
|
|
@@ -6,6 +6,8 @@ layout: docs
|
|
|
6
6
|
|
|
7
7
|
Define experiences in code with `createExperience()`, or load them from the dashboard at runtime with the [Dynamic Experiences Plugin](./plugins/dynamic-experiences).
|
|
8
8
|
|
|
9
|
+
The execution flow below targets a DOM selector and renders into it. A Page experience instead renders through a named slot: target it to `.picked-slot-<name>` and render `<ExperienceSlot name="<name>" />` on your server. Use the slot path for SEO-critical, server-rendered Pages; use the selector path for browser-rendered Flows. See [Server-Side Rendering](/sdk/ssr) and [Slots & Placements](/sdk/ssr/slots-and-placements).
|
|
10
|
+
|
|
9
11
|
## Creating experiences
|
|
10
12
|
|
|
11
13
|
```ts
|
package/dist/docs/sdk/index.md
CHANGED
|
@@ -28,11 +28,21 @@ picked.init()
|
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
```ts [Server]
|
|
31
|
-
|
|
31
|
+
import { createClient } from '@picked/client'
|
|
32
|
+
import { serialize } from '@picked/client/server'
|
|
33
|
+
|
|
34
|
+
const client = createClient({ projectId: 'your-project-id' })
|
|
35
|
+
|
|
36
|
+
const snapshot = await serialize(client, {
|
|
37
|
+
projectConfig,
|
|
38
|
+
attributes: { location: { pathname: '/promo' } },
|
|
39
|
+
})
|
|
32
40
|
```
|
|
33
41
|
|
|
34
42
|
:::
|
|
35
43
|
|
|
44
|
+
The `@picked/client/server` entry holds the server-only helpers, such as `serialize()`, that produce an SSR snapshot. It is a separate entry point so browser bundles never include it. See [Server-Side Rendering](/sdk/ssr).
|
|
45
|
+
|
|
36
46
|
:::tip
|
|
37
47
|
If you’re running Picked in a browser environment, access the Picked client in the console with `window.Picked`
|
|
38
48
|
:::
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: docs
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Server-Side Rendering
|
|
6
|
+
|
|
7
|
+
Render Picked Pages on your server for SEO, then hydrate them on the client. This page shows the end-to-end shape; the following pages cover each piece.
|
|
8
|
+
|
|
9
|
+
## Why server-side rendering
|
|
10
|
+
|
|
11
|
+
Pages are SEO-critical, so their content must be in the HTML your server returns. Crawlers and social unfurls read that HTML directly and do not wait for client-side JavaScript. Rendering a Page on the server puts its content in the initial response, then the client takes over.
|
|
12
|
+
|
|
13
|
+
## The flow
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
projectConfig (Delivery API)
|
|
17
|
+
-> serialize(client, params) on the server -> PickedSnapshot
|
|
18
|
+
-> embed snapshot in HTML
|
|
19
|
+
-> client seeds from snapshot -> renders the same markup -> hydrates
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
1. Fetch your published `projectConfig` from the [Delivery API](/delivery-api).
|
|
23
|
+
2. Call `serialize()` on the server. It evaluates which experiences match the URL, fetches their content, and returns a plain-JSON `PickedSnapshot`. See [Server Snapshot](/sdk/ssr/serialize).
|
|
24
|
+
3. Embed the snapshot in your HTML payload.
|
|
25
|
+
4. On the client, seed a Picked client from the snapshot so the first paint matches the server markup, then hydrate. See [Rendering & Hydration](/sdk/ssr/rendering-and-hydration).
|
|
26
|
+
|
|
27
|
+
Because the snapshot carries the exact config the server selected with and the memoized condition results, the client's first render reuses the server's decisions instead of re-evaluating them.
|
|
28
|
+
|
|
29
|
+
## React vs Svelte at a glance
|
|
30
|
+
|
|
31
|
+
:::code-group
|
|
32
|
+
|
|
33
|
+
```tsx [React]
|
|
34
|
+
import { PickedProvider, ExperienceSlot } from '@picked/react'
|
|
35
|
+
|
|
36
|
+
<PickedProvider options={options} snapshot={snapshot}>
|
|
37
|
+
<ExperienceSlot name="page" />
|
|
38
|
+
</PickedProvider>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```svelte [Svelte]
|
|
42
|
+
<script lang="ts">
|
|
43
|
+
import { ExperienceSlot, setPickedContext } from '@picked/svelte'
|
|
44
|
+
|
|
45
|
+
setPickedContext({ options, snapshot })
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<ExperienceSlot name="page" />
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
:::
|
|
52
|
+
|
|
53
|
+
## The server entry point
|
|
54
|
+
|
|
55
|
+
The server helpers live in a dedicated entry so browser bundles never pull them in:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { serialize, registerProjectConfig } from '@picked/client/server'
|
|
59
|
+
import type { PickedSnapshot } from '@picked/client/server'
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Continue to [Server Snapshot](/sdk/ssr/serialize) for the `serialize()` reference, [Rendering & Hydration](/sdk/ssr/rendering-and-hydration) for wiring React and Svelte, and [Slots & Placements](/sdk/ssr/slots-and-placements) for how a slot selects experiences. New to the SDK? Start with [Installation](/sdk).
|