About how Picked works
What happens between the moment an editor clicks Publish and the moment a visitor sees the result? This page follows one change through the whole system.
The shape of it
Picked splits into two parts that meet at a published snapshot.
Author (dashboard) -> Publish -> Delivery API (config + content)
-> SDK reads it -> conditions decide -> component renders
On the left, an editor works in a workspace. Nothing they do reaches a visitor. On the right, the SDK in your application reads what was published and renders it. The snapshot between them is immutable, so each part moves at its own speed. An editor publishes with no deployment, and you deploy with no change to the content.
What happens when you publish
Picked takes the current workspace and makes an immutable snapshot of it. The result is a version, and a project serves exactly one version at a time, the live one. Nothing in a published version can change later. A roll-back moves the live pointer of the project to an earlier version, and it changes no data.
That is why the dashboard has a draft state at all. The workspace holds the work that is still in progress. The version holds what your visitors get.
What the SDK does at startup
createClient() builds the client, and client.start() runs it. Between those two calls
nothing happens yet.
- Options resolve. The dynamic experiences plugin reads the configuration of your
project from the source that its
modenames. That is the data in your bundle, or a request to the Delivery API. A preview token replaces both. - Everything registers. The plugin registers every feature flag, condition, field, step, and experience from that configuration. You declare none of them in code.
- Preconditions run. A precondition asks whether Picked should start at all. A condition asks whether this visitor is eligible.
- Experiences resolve. Each experience evaluates its condition and looks for its target element in the page.
- Components render. An experience whose condition is true, and whose target exists, renders the component you registered under its component name.
Step 1 is why the Vite plugin exists: it embeds the options at build time, so production makes no request at startup.
Why the browser and the server can read from different places
The two ends of a server-rendered page want different things. Your server wants a new version to reach visitors at once. That needs a request to the API. The browser wants no request at all. That needs the data in your bundle.
The mode option belongs to one end only, so you can have both. Give 'api' to your
server and 'bundle' to your browser.
Each end stays consistent with itself. The configuration and the content that one end reads always come from the same source. Picked gives them the same version.
The two ends can still hold different versions. An editor publishes a workspace between your build and the request, which moves the server to a newer version than the bundle. The snapshot from the server then wins. The browser keeps it, and drops the configuration that it read.
The reason is that the server made the page from that snapshot. To register a different
configuration over the top makes the page and the client show different data. One
version behind is the safer of the two results. The client records the two version IDs on
client.versionSkew, and the devtools show them.
Why the flow is a graph
An experience is not a list of screens. It is a directed graph, and the SDK walks it.
The origin node is the entry point. From there each edge leads to a step, an action, or a condition. A step waits for the visitor. An action runs a side effect and does not interrupt them. A condition evaluates a rule and sends the flow down one of two paths.
The walk is what makes a branch cost so little. To show a different screen to a different visitor you add a condition node, and not a second copy of the flow. The same step can appear in several flows, because a step belongs to the project and the graph only points at it.
How an action decides which way to go
An action node can carry a success branch and a failure branch, and the SDK picks between them from the outcome of your handler:
- The handler throws, or its promise rejects: the flow takes the failure branch.
- The handler resolves to any value at all, and that includes
false: the flow takes the success branch.
This difference matters more than it looks. A valid negative result is not a failure. An
endpoint that answers 200 with { serviced: false } succeeded — it told you something
true. Record that value as a response and branch on it with a condition. Keep the failure
branch for a request that did not complete.
Get this wrong and your failure branch holds two different things at once: a network error, and a visitor who lives outside your service area.
Where content comes in
A flow renders in the browser. A page renders on your server, from content held in the Picked CMS, so a search engine reads it in the first response. Both are experiences, and both go through the same publish.
The difference is only the place where each one renders, and that follows from what the content is for. See About pages and flows and About server-side rendering.
Related
- Add Picked to your application — the whole path, in practice.
- About workspaces and versions — draft, live, and roll-back.
- Delivery API — what the published snapshot looks like on the wire.
