External content
Why use content from extrenal APIs in React Bricks?
React Bricks is a great authoring tool for your content creators, but sometimes:
- You need structured content (entities with relationships) to reuse across pages. Headless CMSs solve this problem very well.
- You have existing content in your headless CMS that serves as a single source of truth.
- You have an e-commerce (for example on Shopify) and you need to get data from the e-commerce inside of React Bricks bricks. React Bricks is great for editing product landing pages.
Video with fetch based on Page data (slug)
Video with fetch based on a brick's side edit props
A. Get external content at Brick level
Since v4.2.0, with React Server Components (RSC) support, getting external data has become much easier, even if you are not using RSC.
It is now possible to fetch at brick level, instead of only at page level (based on the pageType
configuration).
The getExternalData
function
To get external data in a brick, add to the schema
a getExternalData
function, which should be an async function fetching the data, returning a promise that resolves to an object.
The getExternalData
function gets the Page object and the Brick's props as arguments. The returned object will be merged to the component's props, so that you can render the values from the external APIs in your brick.
interface IBlockType<T = Props> {
...
getExternalData?: (
page: Page,
brickProps?: T,
args?: any
) => Promise<Partial<T>>
}
This approach works both in the frontend (where it might leverage RSC, if you are using the RSC-compatible library) and in the Admin interface, where the bricks are always rendered on the client, as they are interactive.
Example of fetch based on a brick's sidebar control:
import { types } from 'react-bricks/rsc'
import classNames from 'classnames'
interface StockQuoteProps {
symbol: string
stockResult: {
c: number
d: number
dp: number
h: number
l: number
}
}
const StockQuote: types.Brick<StockQuoteProps> = ({ symbol, stockResult }) => {
if (!stockResult) {
return (
<div className="text-center text-red-500 underline text-xl">
Symbol not found!
</div>
)
}
const { c, d, dp, h, l } = stockResult
return (
<div className="my-12 w-52 mx-auto rounded-full border px-6 py-3">
<div className="flex items-center justify-between">
<div className="text-black font-bold">{symbol}</div>
<div className="text-black font-bold">{c?.toFixed(2)}</div>
</div>
<div className="flex items-center justify-between">
<div
className={classNames(
'text-sm font-bold',
dp >= 0 ? 'text-green-500' : 'text-red-500'
)}
>
{dp?.toFixed(2)}%
</div>
<div>
<input
type="range"
readOnly
min={l}
max={h}
value={c}
className="w-24 h-1 bg-gray-200 dark:bg-gray-700 rounded-lg appearance-none [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-2
[&::-webkit-slider-thumb]:h-2 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-blue-500"
/>
</div>
</div>
</div>
)
}
StockQuote.schema = {
name: 'stock-quote',
label: 'Stock Quote',
getExternalData: async (page, brickProps) => {
const response = await fetch(
`https://finnhub.io/api/v1/quote?symbol=${brickProps?.symbol}&token=<Your_FinnHub_Token>`,
{ next: { revalidate: 10 } }
)
const data = await response.json()
return {
stockResult: {
c: data.c,
d: data.d,
dp: data.dp,
l: data.l,
h: data.h,
},
} as Partial<StockQuoteProps>
},
// Sidebar Edit controls for props
sideEditProps: [
{
name: 'symbol',
label: 'Symbol',
type: types.SideEditPropType.Text,
},
],
}
export default StockQuote
Example of fetch based on a Page custom field's value:
...
Product.schema = {
...
getExternalData: async (page, brickProps) => {
const res = await fetch(`https://externalapi/products/${page.customValues.productId}`)
const product = await res.json()
return ({
productName: product.name
productImage: product.imageUrl
})
}
}
B. Get external content at Page level
You can also fetch external data for every page of a certain pageType
.
In this case, you define the getExternalData
function on the pageType
definition object and then map the external data on the page to the brick's props.
1. Get external data
On each pageType
, you can define your getExternalData
function.
It takes a Page as argument (so that you have access to the slug, or any custom fields) and it must return a Promise that resolves to an object. That object will be put on the page, in externalData
.
You can then access external data:
- using the
usePageValues
hook (if you just need to render external data as it is); - using the
mapExternalDataToProps
, in order to be able to override this data via visual editing: see the next paragraph.
See also pageType
2. Map external data to the bricks' props
The simpler way to use external content is using the mapExternalDataToProps
on your bricks' schema
. Each brick can map the external data to its props.
The mapExternalDataToProps
function has 2 arguments:
- External data
- Brick's props
You return an object with the new props you want on your content block.
See also brick's schema
Examples
1. Read only
If you just want to render a product title from an e-commerce, you can do this:
mapExternalDataToProps: (externalData, bricksProps) => ({
title: externalData.title,
})
Now you can just render {title}
in your JSX.
2. Visually override external content
If you want to be able to replace the title from the e-commerce data source, you can do this:
mapExternalDataToProps: (externalData, bricksProps) => ({
title:
bricksProps.title && Plain.serialize(bricksProps.title)
? bricksProps.title
: externalData.title,
})
In this case, in your JSX, you can have a Text component from React Bricks, like this:
<Text propName="title" />
If you change the title, you will save your version (for example a title best suited for a marketing landing page). As soon as you delete the title you are back from the official title from your "source of truth".
Pass pageTypes to the fetchPage function
If you come from a starter before v3.1 came out, you need to pass the
pageTypes
from React Bricks configuration to thefetchPage
function. That's because fetchPage cannot access the ReactBricks context (being outside of the React context), but it needs pageTypes to call yourgetExternalData
function after fetching the page.See also fetchPage