Components and slots in code

Developer preview

This feature is in developer preview. Use with caution as it may change unexpectedly. For more information, contact us.

Developer Preview

This page documents the developer preview of the SDK, version 20.81.1-alpha.25.sha-f076f9b857.

This page continues Core concepts. It shows how components, parameters and slots become code in your app.

Each Uniform component gets its props through the ComponentProps<TParameters, TSlotNames> type. Components are React Server Components by default.

PropTypeDescription
typestringThe component type, for example "hero".
variantstring | undefinedThe component variant.
parametersTParametersThe parameter values. Each value is a ComponentParameter<T>.
slotsRecord<TSlotNames, SlotDefinition>The child components, by slot name.
componentComponentContextThe component data: _id, _parentId, slotName and slotIndex.
contextCompositionContextThe composition data: _id, type, state, isContextualEditing, matchedRoute, dynamicInputs and pageState.

Give each parameter the type ComponentParameter<T>:

components/hero.tsx

import { type ComponentParameter, type ComponentProps, UniformRichText, UniformText, } from "@uniformdev/next-app-router/component"; import type { ParameterRichTextValue } from "@uniformdev/richtext"; type HeroParameters = { title?: ComponentParameter<string>; description?: ComponentParameter<ParameterRichTextValue>; }; export const HeroComponent = ({ parameters: { title, description }, component, }: ComponentProps<HeroParameters>) => ( <section> {title ? <UniformText component={component} parameter={title} as="h1" placeholder="Enter a title" /> : null} {description ? ( <UniformRichText component={component} parameter={description} placeholder="Enter a description" /> ) : null} </section> );

UniformText and UniformRichText require a parameter object. Thus the example renders them only when the parameter exists.

warning

Make all parameters optional with ?. A parameter can be undefined at runtime, also when the component definition marks it as required. For example, an author adds a component and does not fill in its parameters.

To read the value of a parameter, use the value property:

type BannerParameters = { title?: ComponentParameter<string>; linkUrl?: ComponentParameter<string>; isVisible?: ComponentParameter<boolean>; }; export const BannerComponent = ({ parameters: { title, linkUrl, isVisible }, }: ComponentProps<BannerParameters>) => { if (isVisible?.value === false) return null; return ( <a href={linkUrl?.value ?? "#"}> <h2>{title?.value ?? "Default title"}</h2> </a> ); };

UniformText renders a text parameter. In Canvas, authors can edit the text in the page.

<UniformText component={component} // Required: the ComponentContext from props parameter={title} // Required: a ComponentParameter<string> as="h1" // Optional: the HTML element (default: "span") className="text-xl" // Optional: HTML attributes go to the element placeholder="Enter a title" // Optional: the text in Canvas when the value is empty isMultiline={false} // Optional: multi-line edit (default: false) render={(value) => value?.toUpperCase()} // Optional: changes the value before render />

UniformText renders nothing when the parameter is missing or is not a text parameter. It also applies conditional values that use quirks. It is a client component.

UniformRichText renders a rich text parameter:

<UniformRichText component={component} // Required: the ComponentContext from props parameter={description} // Required: a ComponentParameter<ParameterRichTextValue> as="div" // Optional: the wrapper element (default: "div") className="prose" // Optional: HTML attributes go to the wrapper placeholder="Enter text" // Optional: the text in Canvas when the value is empty resolveRichTextRenderer={customResolver} // Optional: custom node renderers />

The default link renderer adds the custom link attributes and anchors that authors set. If you replace the link node renderer, use linkParamValueToAnchorProps from @uniformdev/richtext to keep this behavior. Refer to rich text parameter utilities.


A slot contains the child components of a component. For example, a page component can have the slots header, content and footer.

components/page.tsx

import { type ComponentProps, UniformSlot } from "@uniformdev/next-app-router/component"; type PageSlots = "header" | "content" | "footer"; export const PageComponent = ({ slots }: ComponentProps<unknown, PageSlots>) => ( <> <header> <UniformSlot slot={slots.header} /> </header> <main> <UniformSlot slot={slots.content} /> </main> <footer> <UniformSlot slot={slots.footer} /> </footer> </> );

To wrap each item, give UniformSlot a function as its child:

<UniformSlot slot={slots.content}> {({ child, _id, key, slotName, slotIndex }) => ( <div key={key} data-id={_id} className="my-4 border-b pb-4"> {child} </div> )} </UniformSlot>

A slot has the items in slot.items. An item can be null. UniformSlot does not check for null items, and throws an error for one. getUniformSlot and the example below skip them:

export const ListComponent = ({ slots }: ComponentProps<unknown, "content">) => ( <ul> {slots.content.items.map((item) => (item ? <li key={item._id}>{item.component}</li> : null))} </ul> );

getUniformSlot returns the rendered slot items as an array of ReactNode. It skips null items:

import { getUniformSlot } from "@uniformdev/next-app-router/component"; const items = getUniformSlot({ slot: slots.content }); // ReactNode[] | undefined

Use it when you must count the items, or do an array operation before you render them.