# Components and slots in code with the Next.js App Router SDK

> The code side of the core concepts: how a Uniform component, its parameters and its slots become props of your React component.

Source: https://docs.uniform.app/docs/sdk/nextjs-app-router/components

> **Developer Preview:**
>
> This page documents the developer preview of the SDK, version `20.81.1-alpha.25.sha-f076f9b857`.

This page continues [Core concepts](https://docs.uniform.app/docs/sdk/nextjs-app-router/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.

## Component props

| Prop | Type | Description |
| --- | --- | --- |
| `type` | `string` | The component type, for example `"hero"`. |
| `variant` | `string \| undefined` | The component variant. |
| `parameters` | `TParameters` | The parameter values. Each value is a `ComponentParameter<T>`. |
| `slots` | `Record<TSlotNames, SlotDefinition>` | The child components, by slot name. |
| `component` | `ComponentContext` | The component data: `_id`, `_parentId`, `slotName` and `slotIndex`. |
| `context` | `CompositionContext` | The composition data: `_id`, `type`, `state`, `isContextualEditing`, `matchedRoute`, `dynamicInputs` and `pageState`. |

## Parameters

Give each parameter the type `ComponentParameter<T>`:

`components/hero.tsx`

```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:

```tsx
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

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

```tsx
<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

`UniformRichText` renders a rich text parameter:

```tsx
<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](https://docs.uniform.app/docs/guides/models/components/parameters#link) 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](https://docs.uniform.app/docs/sdk/parameter-utilities/rich-text-parameters#custom-rich-text-node-renderers).

---

## Slots

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

### UniformSlot

`components/page.tsx`

```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>
  </>
);
```

### Wrap each slot item

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

```tsx
<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>
```

### Use the slot items directly

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:

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

### getUniformSlot

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

```tsx
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.
