Skip to main content

External content

Why use external content 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

How to get external content

Since v4.2.0, with React Server Components (RSC) support, getting external data has become much easier, even if you are not using RSC.

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
})
}
}

The old implementation, based on getExternalData and mapExternalDataToProps, is still available and we have no plans to remove it, but we suggest to use the new way with getExternalData on the brick. You can still find here the "old way" documentation, which we consider to be deprecated.