External Data Sources
There are several different approaches for loading external data into a Puck component.
It's possible for Puck components to load their own data internally on the client, or on the server using React server components. This doesn't require any Puck configuration.
If you want to provide the user a way to select the data, you can use the external
field type.
Selecting external data
The external
field type allows users to select tabular data from a third-party data source, like a headless CMS. This will load the data once and save it into the data payload.
const config = {
components: {
Example: {
fields: {
data: {
type: "external",
fetchList: async () => {
// Query an API for a list of items
const items = await fetch(`/api/items`).then((res) => res.json());
// [
// { title: "Hello, world", description: "Lorem ipsum 1" },
// { title: "Goodbye, world", description: "Lorem ipsum 2" },
// ];
return items;
},
},
},
render: ({ data }) => {
if (!data) {
return "No data selected";
}
return (
<>
<b>{data.title}</b>
<p>{data.description}</p>
</>
);
},
},
},
};
You can also use the showSearch
parameter to show a search input to the user.
Data syncing
To keep the data in sync with the external source, we can combine the external
field with the resolveData
function.
This technique re-fetches the content every time the page is loaded, or the resolveAllData
utility is called.
const config = {
components: {
Example: {
fields: {
data: {
type: "external",
fetchList: async () => {
// Query an API for a list of items
const items = await fetch(`/api/items`).then((res) => res.json());
// [
// { title: "Hello, world", id: 0 },
// { title: "Goodbye, world", id: 1 },
// ];
return items;
},
},
},
resolveData: async ({ props }, { changed }) => {
if (!props.data) return { props };
// Don't query unless `data` has changed since resolveData was last run
if (!changed.data) return { props };
// Re-query the API for a particular item
const latestData = await fetch(`/api/items/${props.data.id}`).then(
(res) => res.json()
);
// { title: "Hello, world", description: "Lorem ipsum 1", id: 0 }
return {
props: {
// Update the value for `data`
data: latestData,
},
};
},
// ...
},
},
};
Hybrid authoring
Hybrid authoring enables users to edit fields inline, or populate those fields with data from an external source.
This can be achieved by mapping the data from data.title
to title
in resolveData
, and marking the field as read-only.
const config = {
components: {
Example: {
fields: {
data: {
// ...
},
title: {
type: "text",
},
},
resolveData: async ({ props }, { changed }) => {
// Remove read-only from the title field if `data` is empty
if (!props.data) return { props, readOnly: { title: false } };
// Don't query unless `data` has changed since resolveData was last run
if (!changed.data) return { props };
return {
props: {
title: props.data.title,
readOnly: { title: true },
},
};
},
render: ({ title }) => <b>{title}</b>,
},
},
};