← Back to all how-tos

Integrate external data

Fetch external data in bricks

Fetch data directly inside a brick with getExternalData and merge the API response into the brick props.

Estimated time: 7 minRaw Markdown

In this how-to, you'll learn how to fetch data from an external API directly inside a brick.

This is the simplest way to integrate external data in React Bricks when a single brick is responsible for rendering that data.

This fetching happens server-side. In Next.js App Router projects, React Bricks does this by leveraging Server Components.

That means:

  • the external API call is not exposed in the browser
  • the fetched content is already present in the rendered HTML, which is better for SEO
  • users see the final content immediately, without waiting for a client-side fetch after page load

Docs reference: Get data from external APIs

When brick-level fetching is a good fit

Fetching inside a brick works well when:

  • only one brick needs the external data
  • the request depends on that brick's own props
  • you want the brick to stay self-contained

For example, a brick may:

  • fetch a Pokemon chosen in the sidebar
  • load product data using a productId stored on the page
  • call a third-party API based on a slug or other page value

If several bricks need the same external data, fetching at page type level is usually a better choice. We'll cover that in the next guide.

Use getExternalData on the brick schema

To fetch external data in a brick, add a getExternalData function to the brick's schema.

This function is async and should return an object.

React Bricks merges that returned object into the brick props, so the fetched data becomes available inside the component.

The function receives:

  • page: the current page object
  • brickProps: the current props for that brick
  • args: optional extra arguments passed from fetchPage

Docs reference: Connect external APIs

The TypeScript signature is:

getExternalData?: (
  page: Page,
  brickProps?: T,
  args?: any
) => Promise<Partial<T>>

Example: fetch data from a brick prop

Let's create a Pokemon brick.

The editor chooses a Pokemon name in the sidebar, and the brick fetches the data from the public Pokemon API.

This is a nice example for a how-to because it works without any API key.

import React from 'react'
import { types } from 'react-bricks/rsc'
 
interface PokemonProps {
  pokemonName: string
  id: number
  name: string
  height: number
  weight: number
  imageUrl: string
}
 
const Pokemon: types.Brick<PokemonProps> = ({
  id,
  name,
  height,
  weight,
  imageUrl,
}) => {
  if (!id || !name || !height || !weight || !imageUrl) {
    return null
  }
 
  return (
    <div className="my-6 pb-6 container max-w-3xl mx-auto border-2 border-slate-200">
      <div className="p-2 bg-slate-100 mb-6">
        <p className="text-sm text-slate-700 uppercase tracking-widest font-bold text-center mb-1">
          Test external data
        </p>
      </div>
      <img src={imageUrl} className="mx-auto w-36 mb-4" />
 
      <h1 className="text-5xl font-extrabold text-center mb-6">{name}</h1>
 
      <p className="text-center">
        #{id} - Height {height / 10} m - Weight {weight / 10} Kg
      </p>
    </div>
  )
}
 
Pokemon.schema = {
  name: 'pokemon',
  label: 'Pokemon',
  previewImageUrl: `/bricks-preview-images/pokemon.png`,
  getDefaultProps: () => ({
    pokemonName: 'pikachu',
  }),
 
  getExternalData: (page, brickProps) =>
    fetch(`https://pokeapi.co/api/v2/pokemon/${brickProps?.pokemonName}`)
      .then((response) => response.json())
      .then((data) => ({
        id: data.id,
        name: data.name,
        height: data.height,
        weight: data.weight,
        imageUrl: `https://img.pokemondb.net/artwork/large/${data.name}.jpg`,
      }))
      .catch(() => {
        return {
          id: 0,
          name: '',
          height: 0,
          weight: 0,
          imageUrl: '',
        }
      }),
 
  sideEditProps: [
    {
      name: 'pokemonName',
      label: 'Pokemon Name',
      type: types.SideEditPropType.Text,
      helperText:
        'Enter a valid Pokemon name, like "pikachu" or "charizard" and save.',
    },
  ],
}
 
export default Pokemon

Here is what happens:

  • the editor sets the pokemonName prop in the sidebar
  • getExternalData reads brickProps.pokemonName
  • the API response is transformed into the props the brick needs
  • the fetched values are merged into the brick props
  • the component renders the fetched values

This pattern is very useful when the request depends on a value chosen per brick instance.

If you want to see an authenticated external API example, the official docs also show a stock quote example: Connect external APIs

Example: fetch data from a page custom field

Sometimes the API request should be based on a page-level value rather than a brick prop.

For example, a product page type may have a custom field called productId.

In that case, the brick can read the value from page.customValues.

Product.schema = {
  name: 'product',
  label: 'Product',
 
  getExternalData: async (page) => {
    const response = await fetch(
      `https://example.com/api/products/${page.customValues.productId}`
    )
 
    const product = await response.json()
 
    return {
      productName: product.name,
      productImage: product.imageUrl,
    }
  },
}

This works well when:

  • the external record belongs to the page as a whole
  • the page type stores a stable identifier such as productId
  • one brick is responsible for rendering that external data

If you haven't added page custom fields yet, see: Add custom fields

A note about args

The third argument of getExternalData is args.

You can use it to receive additional values passed from fetchPage, such as query string parameters, or other request-time context.

That can be helpful when the external API call depends on runtime input instead of only page values or brick props.

For a complete real-world example of using args with dynamic route params, see: Generate pages from a visual template and external data

Tips for production use

When integrating real APIs, it helps to keep a few things in mind:

  • return only the fields the brick actually needs
  • handle missing or invalid API responses gracefully
  • avoid exposing secrets directly in client-side code
  • use caching or revalidation when your framework supports it

In the example above, { next: { revalidate: 10 } } is a Next.js caching option that lets you refresh the external data periodically instead of fetching on every request.

This server-side approach is especially useful for SEO-sensitive content, because search engines receive the fetched content as part of the initial HTML response.

When to move fetching to page level

Brick-level fetching is the easiest approach, but it is not always the best one.

If multiple bricks on the same page need the same external response, fetching once at page type level is often cleaner and more efficient.

In that case, the page type performs the fetch and each brick maps the shared external data to its own props.

We'll use that approach in the next guide.