Skip to main content

External content

Why use content from extrenal APIs in React Bricks?

React Bricks is a great authoring tool for your content creators, but sometimes:

  1. You need structured content (entities with relationships) to reuse across pages. Headless CMSs solve this problem very well.
  2. You have existing content in your headless CMS that serves as a single source of truth.
  3. 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:

StockQuote
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.tsx

...
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:

  1. External data
  2. 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 the fetchPage function. That's because fetchPage cannot access the ReactBricks context (being outside of the React context), but it needs pageTypes to call your getExternalData function after fetching the page.

See also fetchPage