Content
The client holds every content record in one index, keyed by the id. The hooks on this page read that index.
The content record
interface Content {
id: string
contentModelKey?: string | null
data: Record<string, unknown>
}| Property | Type | Description |
|---|---|---|
id | string | The content entry id. |
contentModelKey | string | null | The Key of the Model that the entry belongs to. |
data | Record<string, unknown> | The field values, keyed by the field key from the schema. |
The client registers the content array and the references array of the
experience content response into the same
index. Your code can therefore read a referenced entry by its own id.
Read the content of a step
useStepContent() in React, and getStepContent() in Svelte, give the data of the
entry that the current step points at.
import { useStepContent } from '@picked/react/hooks'
export function Hero() {
const content = useStepContent()
return <h1>{String(content?.headline ?? '')}</h1>
}<script lang="ts">
import { getStepContent } from '@picked/svelte/hooks'
const content = getStepContent()
</script>
<h1>{$content?.headline ?? ''}</h1>useStepContent() returns the data, and React re-renders the component when the data
changes. getStepContent() returns a store, which you read with the $ prefix.
Read the record of a step
useStepLayoutContent() in React, and getStepLayoutContent() in Svelte, give the
whole record, which includes contentModelKey. They read the same record as
useStepContent(), both inside a step and in an experience that <ExperienceSlot>
renders. isContentOfModel narrows the record to one model. See
Type generation.
import { isContentOfModel } from '@picked/client'
import { useStepLayoutContent } from '@picked/react/hooks'
export function Page() {
const record = useStepLayoutContent()
if (record && isContentOfModel(record, 'landing-page')) {
return <h1>{String(record.data.headline ?? '')}</h1>
}
return null
}<script lang="ts">
import { isContentOfModel } from '@picked/client'
import { getStepLayoutContent } from '@picked/svelte/hooks'
const record = getStepLayoutContent()
</script>
{#if $record && isContentOfModel($record, 'landing-page')}
<h1>{$record.data.headline ?? ''}</h1>
{/if}Read a record by id
useContent(id) and getContent(id) return a store of one record. The store holds
null until the client registers that record.
import { useContent, useStoreGlobal } from '@picked/react/hooks'
export function Banner() {
const banner = useStoreGlobal(useContent('entry_42'))
return <h1>{String(banner?.data.headline ?? '')}</h1>
}<script lang="ts">
import { getContent } from '@picked/svelte/hooks'
const banner = getContent('entry_42')
</script>
<h1>{$banner?.data.headline ?? ''}</h1>Resolve a reference field
A Reference field holds a pointer,
and not the record. useReference() and getReference() resolve the pointer against the
index.
import { useReference, useStepContent, useStoreGlobal } from '@picked/react/hooks'
export function ProductCategory() {
const content = useStepContent()
const category = useStoreGlobal(useReference(content?.category))
return <span>{String(category?.data.name ?? '')}</span>
}<script lang="ts">
import { getStepContent, getReference } from '@picked/svelte/hooks'
const content = getStepContent()
const category = getReference(content.map((c) => c?.category ?? null, { skipVoid: false }))
</script>
<span>{$category?.data.name ?? ''}</span>For a repeatable Reference field, use useReferences() and getReferences(). They give
a list in field order.
import { useReferences, useStepContent, useStoreGlobal } from '@picked/react/hooks'
export function ProductCategories() {
const content = useStepContent()
const categories = useStoreGlobal(useReferences(content?.categories))
return (
<ul>
{categories.map((category) => (
<li key={category.id}>{String(category.data.name ?? '')}</li>
))}
</ul>
)
}<script lang="ts">
import { getStepContent, getReferences } from '@picked/svelte/hooks'
const content = getStepContent()
const categories = getReferences(content.map((c) => c?.categories ?? null, { skipVoid: false }))
</script>
<ul>
{#each $categories as category (category.id)}
<li>{category.data.name ?? ''}</li>
{/each}
</ul>The list holds no empty places. A pointer that Picked cannot resolve is absent from the list, so each item is a record.
Reference hooks
| Hook | Returns | When the target is absent |
|---|---|---|
useReference(value) | Store<Content | null> | null |
useReferences(value) | Store<Content[]> | The item is absent from the list. |
getReference(value) | Store<Content | null> | null |
getReferences(value) | Store<Content[]> | The item is absent from the list. |
Each one accepts the raw field value, or a store of it. A single value that goes to
useReferences() or getReferences() gives a list of one.
A target is absent for three reasons. Somebody deleted the entry, the entry belongs to another workspace, or the chain of references is deeper than the limit. See About content references.
Read a pointer directly
@picked/client gives you the readers behind the hooks, for the code that runs outside
React and Svelte.
| Export | Returns | Description |
|---|---|---|
isContentReference(value) | boolean | Whether the value is a pointer. |
readReferenceId(value) | string | null | The target id of a single pointer. |
readReferenceIds(value) | string[] | Every target id, for a repeatable field. |
deriveReference(contents, value) | Store<Content | null> | The store behind useReference and getReference. |
deriveReferences(contents, value) | Store<Content[]> | The store behind useReferences and getReferences. |
import { createClient, readReferenceIds } from 'picked'
const client = createClient({ projectId: 'proj_123' })
const content = client.contents.getById('entry_789').getState()
const categoryIds = readReferenceIds(content?.data.categories)Related
- About content references — why a reference is a pointer.
- How to read a referenced entry
- Experience content endpoint — the wire shape.
- Server snapshot — content on a server render.
