Skip to main content

Migration guide v3 > v4

Relationship fields

The value returned from a relationship field (as a prop on the brick for a sideEditProp or on the customValues for a customField) now contains only the ID of the related entity.

If you need to get also the complete object for the related entity, you need to specify embedEntity: true in the relationshipOptions.

Example with sidebar control


import { types } from 'react-bricks/frontend'

MyComponent.schema = {
...
sideEditProps: [
{
name: 'categoryId',
type: types.SideEditPropType.Relationship,
label: 'Product Category'
relationshipOptions: {
references: 'category'
multiple: false
embedValues: true
}
}
]
}

Example with pageType-level custom field

import { types } from 'react-bricks/frontend'

const pageTypes: types.IPageType[] = [
{
name: 'product',
pluralName: 'products',
defaultLocked: false,
defaultStatus: types.PageStatus.Published,
getDefaultContent: () => [],
customFields: [
{
name: 'categoryId',
type: types.SideEditPropType.Relationship,
label: 'Product Category'
relationshipOptions: {
references: 'category'
multiple: false
embedValues: true
}
},
],
},
...
]

React Server Components

Since v4.2, React Bricks supports React Server Components (RSC), in the Next.js starters with App Router. When using Server Components, the visual components (Text, RichText, Image, File Repeater, Link) must be imported from react-bricks/rsc and they have a slightly different APIs, as we need to pass them the value of the prop.

1. Text

Add the value prop. You get it from the component's props. The type should be declared as ReactBricks.types.TextValue.

Before

import { Text } from 'react-bricks/frontend'

...
<Text
propName="title"
placeholder="Enter the title"
renderBlock={({ children}) => (
<p className="text-xl">
{children}
</p>
)}
>

After

import { Text } from 'react-bricks/rsc'

...
<Text
propName="title"
value={title}
placeholder="Enter the title"
renderBlock={({ children}) => (
<p className="text-xl">
{children}
</p>
)}
>

2. RichText

Add the value prop. You get it from the component's props. The type should be declared as ReactBricks.types.TextValue.

Before

import { RichText } from 'react-bricks/frontend'

...
<RichText
propName="title"
placeholder="Enter the title"
renderBlock={({ children}) => (
<h1 className="text-xl">
{children}
</h1>
)}
>

After

import { RichText } from 'react-bricks/rsc'

...
<RichText
propName="description"
value={description}
placeholder="Enter the description"
renderBlock={({ children}) => (
<p>
{children}
</p>
)}
allowedFeatures={[types.RichTextFeatures.Bold]},
>

3. Image

Add the source prop. You get it from the component's props. The type should be declared as ReactBricks.types.IImageSource.

Before

import { Image } from 'react-bricks/frontend'

...
<Image propName="avatar" alt="Avatar" />

After

import { Image } from 'react-bricks/rsc'

...
<Image
propName="avatar"
source={avatar}
alt="Avatar"
/>

4. File

Add the source prop. You get it from the component's props. The type should be declared as ReactBricks.types.IFileSource.

Before

import { File } from 'react-bricks/frontend'

...
<File propName="document" />

After

import { File } from 'react-bricks/rsc'

...
<File
propName="document"
source={document}
/>

5. Repeater

Add the items prop. You get it from the component's props. The type should be declared as ReactBricks.types.RepeaterItems.

Before

import { Repeater } from 'react-bricks/frontend'

...
<Repeater propName="features" />

After

import { Repeater } from 'react-bricks/rsc'

...
<Repeater
propName="features"
items={features}
/>

6. Client Bricks

For interactive bricks, which need client hydration, you need to create 2 components: a server wrapper and the client component, in this way:

Map.tsx
import { types, wrapClientComponent } from 'react-bricks/rsc'
import { RegisterComponent } from 'react-bricks/rsc/client'

import MapClient, { MapProps } from './MapClient'

const MAPTILER_ACCESS_TOKEN = '' // Insert access token

const schema: types.IBlockType<MapProps> = {
name: 'map',
label: 'Map',
category: 'contact',
tags: ['contacts', 'map'],
playgroundLinkLabel: 'View source code on Github',
playgroundLinkUrl:
'https://github.com/ReactBricks/react-bricks-ui/blob/master/src/website/Map/Map.tsx',
previewImageUrl: `/bricks-preview-images/map.png`,
getDefaultProps: () => ({
lat: '45.6782509',
lng: '9.5669407',
zoom: '6',
}),
sideEditProps: [
{
name: 'zoom',
label: 'Zoom',
type: types.SideEditPropType.Number,
},
{
name: 'lat',
label: 'Latitude',
type: types.SideEditPropType.Number,
},
{
name: 'lng',
label: 'Longitude',
type: types.SideEditPropType.Number,
},
{
name: 'maptiler',
label: 'MapTiler',
type: types.SideEditPropType.Custom,
show: () => !MAPTILER_ACCESS_TOKEN,
component: () => {
if (!MAPTILER_ACCESS_TOKEN) {
return (
<p className="text-sm">
For better maps, please create a MapTiler free account and set the{' '}
<code className="text-xs">MAPTILER_ACCESS_TOKEN</code> string.
</p>
)
}
return null
},
},
],
}

export default wrapClientComponent({
ClientComponent: MapClient,
RegisterComponent,
schema,
})
MapClient.tsx
'use client'

import { Map, Marker } from 'pigeon-maps'
import { maptiler } from 'pigeon-maps/providers'
import React from 'react'

export interface MapProps {
zoom: string
lat: string
lng: string
mapTilerAccessToken: string
}

export const MapClient: React.FC<MapProps> = ({
lat = '45.6782509',
lng = '9.5669407',
zoom = '10',
mapTilerAccessToken,
}) => {
const mapTilerProvider = React.useCallback(
(x: number, y: number, z: number, dpr?: number | undefined) =>
maptiler(mapTilerAccessToken, 'streets')(x, y, z, dpr),
[mapTilerAccessToken]
)

let mapTilerProviderProp = {}

if (mapTilerAccessToken) {
mapTilerProviderProp = {
provider: mapTilerProvider,
}
}

return (
<Map
center={[parseFloat(lat), parseFloat(lng)]}
height={350}
metaWheelZoom
zoom={parseInt(zoom, 10)}
{...mapTilerProviderProp}
dprs={[1, 2]}
metaWheelZoomWarning="Use ctrl + wheel to zoom!"
attribution={false}
>
<Marker anchor={[parseFloat(lat), parseFloat(lng)]} />
</Map>
)
}

export default MapClient

7. Fetching data from a brick

We can directly fetch external data from a brick, without having to create the getExternalData on a pageType.

In a brick, we can just add to the schema a getExternalData function, which should be an async function, returning a promise that resolves to an object. This object will be merged to the component's props.

See the new External Content documentation.