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.