# Content rendering

Learn how to fetch and render content for your slots.

This guide provides practical examples of how to use the React SDK to fetch and render a [Slot](/explanation/slot) in your application.

## Basic usage

Start by adding the slot using the CLI:

**Command to add a slot to your project**

```sh
croct@latest add slot --example
```

The CLI will prompt you to select the slot you want to add.

Once selected, it takes care of everything — from generating the TypeScript types to adding a working example to your project. You can use this example as a reference for your own implementation.

> **Good to know: How do types and fallback content work?**
>
> The CLI [generates type definitions](/reference/cli/type-generation) so that calls like [`useContent('home-hero@2')`](api/hooks/use-content) return the correct content type with full autocomplete. It also [downloads default content](/reference/cli/fallback-content) that the SDK uses as automatic fallback when dynamic content is unavailable.

### How it works

Let's say you have a slot called `home-hero` for the hero section of your homepage.

To fetch content, you can use either the [`useContent`](api/hooks/use-content) hook or the [`<Slot>`](api/components/slot) component, depending on whether you prefer an imperative or declarative approach.

Below is an example of how to fetch and render content using both methods:

**Hook — JavaScript**

```jsx
import {useContent} from '@croct/plug-react';

export function HomeHero() {
  const {content} = useContent('home-hero');

  return (
    <div>
      <strong>{content.title}</strong>
      <p>{content.subtitle}</p>
      <a href={content.button.link}>{content.button.label}</a>
    </div>
  );
}
```

**Hook — TypeScript**

```tsx
import type {ReactElement} from 'react';
import {useContent} from '@croct/plug-react';

export function HomeHero(): ReactElement {
  const {content} = useContent('home-hero');

  return (
    <div>
      <strong>{content.title}</strong>
      <p>{content.subtitle}</p>
      <a href={content.button.link}>{content.button.label}</a>
    </div>
  );
}
```

**Component — JavaScript**

```jsx
import {Slot} from '@croct/plug-react';

export function HomeHero() {
  return (
    <Slot id="home-hero">
      {({content}) => (
        <div>
          <strong>{content.title}</strong>
          <p>{content.subtitle}</p>
          <a href={content.button.link}>{content.button.label}</a>
        </div>
      )}
    </Slot>
  );
}
```

**Component — TypeScript**

```tsx
import type {ReactElement} from 'react';
import {Slot} from '@croct/plug-react';

export function HomeHero(): ReactElement {
  return (
    <Slot id="home-hero">
      {({content}) => (
        <div>
          <strong>{content.title}</strong>
          <p>{content.subtitle}</p>
          <a href={content.button.link}>{content.button.label}</a>
        </div>
      )}
    </Slot>
  );
}
```

If you are using a server-side rendering framework, you should also provide an initial content for pre-rendering, which will then be personalized on the client.

> **Example: How can I pre-render initial content on the server?**
>
> Pass the [`initial`](api/hooks/use-content#options-initial-prop) option for pre-rendering on the server:
>
> **Hook — JavaScript**
>
> ```diff
> import {useContent} from '@croct/plug-react';
>
> export function HomeHero() {
>   const {content} = useContent('home-hero', {
> +    initial: {
> +      title: 'Welcome to Croct!',
> +      subtitle: 'The easiest way to personalize your application.',
> +      button: {
> +        label: 'Get started',
> +        link: '/signup',
> +      }
> +    },
>   });
>
>   return (
>     <div>
>       <strong>{content.title}</strong>
>       <p>{content.subtitle}</p>
>       <a href={content.button.link}>{content.button.label}</a>
>     </div>
>   );
> }
> ```
>
> **Hook — TypeScript**
>
> ```diff
> import type {ReactElement} from 'react';
> import {useContent} from '@croct/plug-react';
>
> export function HomeHero(): ReactElement {
>   const {content} = useContent('home-hero', {
> +    initial: {
> +      title: 'Welcome to Croct!',
> +      subtitle: 'The easiest way to personalize your application.',
> +      button: {
> +        label: 'Get started',
> +        link: '/signup',
> +      }
> +    },
>   });
>
>   return (
>     <div>
>       <strong>{content.title}</strong>
>       <p>{content.subtitle}</p>
>       <a href={content.button.link}>{content.button.label}</a>
>     </div>
>   );
> }
> ```
>
> **Component — JavaScript**
>
> ```diff
> import {Slot} from '@croct/plug-react';
>
> export function HomeHero() {
>   return (
>     <Slot
>       id="home-hero"
> +      initial={{
> +        title: 'Welcome to Croct!',
> +        subtitle: 'The easiest way to personalize your application.',
> +        button: {
> +          label: 'Get started',
> +          link: '/signup',
> +        }
> +    }}
>     >
>       {({content}) => (
>         <div>
>           <strong>{content.title}</strong>
>           <p>{content.subtitle}</p>
>           <a href={content.button.link}>{content.button.label}</a>
>         </div>
>       )}
>     </Slot>
>   );
> }
> ```
>
> **Component — TypeScript**
>
> ```diff
> import type {ReactElement} from 'react';
> import {Slot} from '@croct/plug-react';
>
> export function HomeHero(): ReactElement {
>   return (
>     <Slot
>       id="home-hero"
> +      initial={{
> +        title: 'Welcome to Croct!',
> +        subtitle: 'The easiest way to personalize your application.',
> +        button: {
> +          label: 'Get started',
> +          link: '/signup',
> +        }
> +      }}
>     >
>       {({content}) => (
>         <div>
>           <strong>{content.title}</strong>
>           <p>{content.subtitle}</p>
>           <a href={content.button.link}>{content.button.label}</a>
>         </div>
>       )}
>     </Slot>
>   );
> }
> ```
>
> To render personalized content on the server, you can use the [`fetchContent`](api/functions#fetchcontent) function or one of our framework-specific SDKs, like the [Next.js SDK](/reference/sdk/nextjs/integration).

Since the content is fetched asynchronously, running the above code would cause the application to suspend. To render a loading state, you can use either a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary or an initial value.

> **Using suspense**
>
> Wrap the component in a `<Suspense>` boundary to handle the load state:
>
> **JavaScript**
>
> ```diff
> import {Suspense} from 'react';
> import {HomeHero} from '../components/HomeHero';
>
> export default function HomePage() {
>   return (
> +    <Suspense fallback="✨ Personalizing content...">
>       <HomeHero />
> +    </Suspense>
>   );
> }
> ```
>
> **TypeScript**
>
> ```diff
> import {type ReactElement, Suspense} from 'react';
> import {HomeHero} from '../components/HomeHero';
>
> export default function HomePage(): ReactElement {
>   return (
> +    <Suspense fallback="✨ Personalizing content...">
>       <HomeHero />
> +    </Suspense>
>   );
> }
> ```

> **Using an initial value**
>
> Specify an initial value to use while the content is loading:
>
> **Hook — JavaScript**
>
> ```diff
> import {useContent} from '@croct/plug-react';
>
> export function HomeHero() {
>   const {content} = useContent('home-hero', {
> +    initial: null,
> +  });
> +
> +  if (content === null) {
> +    return <div>✨ Personalizing content...</div>;
> +  }
>
>   return (
>     <div>
>       <strong>{content.title}</strong>
>       <p>{content.subtitle}</p>
>       <a href={content.button.link}>{content.button.label}</a>
>     </div>
>   );
> }
> ```
>
> **Hook — TypeScript**
>
> ```diff
> import type {ReactElement} from 'react';
> import {useContent} from '@croct/plug-react';
>
> export function HomeHero(): ReactElement {
>   const {content} = useContent('home-hero', {
> +    initial: null,
> +  });
> +
> +  if (content === null) {
> +    return <div>✨ Personalizing content...</div>;
> +  }
>
>   return (
>     <div>
>       <strong>{content.title}</strong>
>       <p>{content.subtitle}</p>
>       <a href={content.button.link}>{content.button.label}</a>
>     </div>
>   );
> }
> ```
>
> **Component — JavaScript**
>
> ```diff
> import {Slot} from '@croct/plug-react';
>
> export function HomeHero() {
>   return (
> +    <Slot id="home-hero" initial={null}>
>       {({content}) => (
> +        content === null
> +        ? <div>✨ Personalizing content...</div>
>         : (
>           <div>
>             <strong>{content.title}</strong>
>             <p>{content.subtitle}</p>
>             <a href={content.button.link}>{content.button.label}</a>
>           </div>
>         )
>       )}
>     </Slot>
>   );
> }
> ```
>
> **Component — TypeScript**
>
> ```diff
> import type {ReactElement} from 'react';
> import {Slot} from '@croct/plug-react';
>
> export function HomeHero(): ReactElement {
>   return (
> +    <Slot id="home-hero" initial={null}>
>       {({content}) => (
> +        content === null
> +        ? <div>✨ Personalizing content...</div>
>         : (
>           <div>
>             <strong>{content.title}</strong>
>             <p>{content.subtitle}</p>
>             <a href={content.button.link}>{content.button.label}</a>
>           </div>
>         )
>       )}
>     </Slot>
>   );
> }
> ```

For more information about the available options, refer to the documentation of the [`useContent`](api/hooks/use-content) hook and [`<Slot>`](api/components/slot) component.

## Typing

If you are using TypeScript, you can use the [`SlotContent`](api/types/slot-content) and [`ComponentContent`](api/types/component-content) types to type variables and props based on slot or component schemas:

```tsx
import type {SlotContent} from '@croct/plug-react';

type HeroProps = SlotContent<'home-hero@1'>;

export function HomeHero(props: HeroProps) {
  return <h1>{props.title}</h1>;
}
```

These types are automatically available when you add slots or components using the CLI. See [Type generation](/reference/cli/type-generation) for details.

## Fault tolerance

> **Auto-provided**
>
> You can skip this step if you added the slot using the Croct CLI, since the fallback content is already included based on the slot's default content. See the [fallback hierarchy](/reference/cli/fallback-content#fallback-hierarchy) for how the SDK resolves content.

You should always provide a [fallback content](/explanation/content/fallback-content) to make your application resilient to unexpected errors, downtime, and network failures.

All you have to do is specify the content you want to use as a fallback:

**Hook — JavaScript**

```jsx
import {useContent} from '@croct/plug-react';

export function HomeHero() {
  const {content} = useContent('home-hero', {
    fallback: {
      title: 'Welcome to Croct!',
      subtitle: 'The easiest way to personalize your application.',
      button: {
        label: 'Get started',
        link: '/signup',
      }
    },
  });

  return (
    <div>
      <strong>{content.title}</strong>
      <p>{content.subtitle}</p>
      <a href={content.button.link}>{content.button.label}</a>
    </div>
  );
}
```

**Hook — TypeScript**

```tsx
import type {ReactElement} from 'react';
import {useContent} from '@croct/plug-react';

export function HomeHero(): ReactElement {
  const {content} = useContent('home-hero', {
    fallback: {
      title: 'Welcome to Croct!',
      subtitle: 'The easiest way to personalize your application.',
      button: {
        label: 'Get started',
        link: '/signup',
      }
    },
  });

  return (
    <div>
      <strong>{content.title}</strong>
      <p>{content.subtitle}</p>
      <a href={content.button.link}>{content.button.label}</a>
    </div>
  );
}
```

**Component — JavaScript**

```jsx
import {Slot} from '@croct/plug-react';

export function HomeHero() {
  return (
    <Slot id="home-hero">
      {({content}) => (
        content === null
        ? (
          <div>
            <strong>Welcome to Croct!</strong>
            <p>The easiest way to personalize your application.</p>
            <a href="/signup">Get started</a>
          </div>
        )
        : (
          <div>
            <strong>{content.title}</strong>
            <p>{content.subtitle}</p>
            <a href={content.button.link}>{content.button.label}</a>
          </div>
        )
      )}
    </Slot>
  );
}
```

**Component — TypeScript**

```tsx
import type {ReactElement} from 'react';
import {Slot} from '@croct/plug-react';

export function HomeHero(): ReactElement {
  return (
    <Slot id="home-hero">
      {({content}) => (
        content === null
        ? (
          <div>
            <strong>Welcome to Croct!</strong>
            <p>The easiest way to personalize your application.</p>
            <a href="/signup">Get started</a>
          </div>
        )
        : (
          <div>
            <strong>{content.title}</strong>
            <p>{content.subtitle}</p>
            <a href={content.button.link}>{content.button.label}</a>
          </div>
        )
      )}
    </Slot>
  );
}
```

The SDK takes care of the rest, ensuring that your application will always have content to render, even if the fetch fails.

## Version control

You can lock a specific slot version to keep the content structure aligned with your application's expectations. This gives your team the freedom to evolve the structure over time without the risk of breaking things.

You can specify the version of the slot by passing a versioned ID in the form `<id>@<version>`. For example, passing `home-hero@2` will fetch the content for the `home-hero` slot in version 2. Not specifying a version number is the same as passing `home-hero@latest`, which will load the latest content.

**Hook — JavaScript**

```jsx
import {useContent} from '@croct/plug-react';

export function HomeHero() {
  const {content} = useContent('home-hero@2');

  return (
    <div>
      <strong>{content.title}</strong>
      <p>{content.subtitle}</p>
      <a href={content.button.link}>{content.button.label}</a>
    </div>
  );
}
```

**Hook — TypeScript**

```tsx
import type {ReactElement} from 'react';
import {useContent} from '@croct/plug-react';

export function HomeHero(): ReactElement {
  const {content} = useContent('home-hero@2');

  return (
    <div>
      <strong>{content.title}</strong>
      <p>{content.subtitle}</p>
      <a href={content.button.link}>{content.button.label}</a>
    </div>
  );
}
```

**Component — JavaScript**

```jsx
import {Slot} from '@croct/plug-react';

export function HomeHero() {
  return (
    <Slot id="home-hero@2">
      {({content}) => (
        <div>
          <strong>{content.title}</strong>
          <p>{content.subtitle}</p>
          <a href={content.button.link}>{content.button.label}</a>
        </div>
      )}
    </Slot>
  );
}
```

**Component — TypeScript**

```tsx
import type {ReactElement} from 'react';
import {Slot} from '@croct/plug-react';

export function HomeHero(): ReactElement {
  return (
    <Slot id="home-hero@2">
      {({content}) => (
        <div>
          <strong>{content.title}</strong>
          <p>{content.subtitle}</p>
          <a href={content.button.link}>{content.button.label}</a>
        </div>
      )}
    </Slot>
  );
}
```

For more information, see [Slot versioning](/explanation/slot#versioning).

## Localization

To support multiple locales, you can use the [`preferredLocale`](api/hooks/use-content#options-preferredlocale-prop) option to specify the locale of the content you want to retrieve. This is usually the locale of the user's browser or account.

> **Good to know**
>
> You can set a [default locale](api/components/croct-provider#defaultpreferredlocale-prop) for your application during SDK initialization, eliminating the need to specify it with each content fetch.

By default, if you do not specify a locale, or if the content is not available in the preferred locale, the content is returned in the [default locale of your workspace](https://app.croct.com/redirect/organizations/-organization-/workspaces/-workspace-/settings).

Here is an example of how to fetch content in a specific locale:

**Hook — JavaScript**

```jsx
import {useContent} from '@croct/plug-react';

export function HomeHero() {
  const {content} = useContent('home-hero', {
    preferredLocale: 'en-ca',
  });

  return (
    <div>
      <strong>{content.title}</strong>
      <p>{content.subtitle}</p>
      <a href={content.button.link}>{content.button.label}</a>
    </div>
  );
}
```

**Hook — TypeScript**

```tsx
import type {ReactElement} from 'react';
import {useContent} from '@croct/plug-react';

export function HomeHero(): ReactElement {
  const {content} = useContent('home-hero', {
    preferredLocale: 'en-ca',
  });

  return (
    <div>
      <strong>{content.title}</strong>
      <p>{content.subtitle}</p>
      <a href={content.button.link}>{content.button.label}</a>
    </div>
  );
}
```

**Component — JavaScript**

```jsx
import {Slot} from '@croct/plug-react';

export function HomeHero() {
  return (
    <Slot id="home-hero" preferredLocale="en-ca">
      {({content}) => (
        <div>
          <strong>{content.title}</strong>
          <p>{content.subtitle}</p>
          <a href={content.button.link}>{content.button.label}</a>
        </div>
      )}
    </Slot>
  );
}
```

**Component — TypeScript**

```tsx
import type {ReactElement} from 'react';
import {Slot} from '@croct/plug-react';

export function HomeHero(): ReactElement {
  return (
    <Slot id="home-hero" preferredLocale="en-ca">
      {({content}) => (
        <div>
          <strong>{content.title}</strong>
          <p>{content.subtitle}</p>
          <a href={content.button.link}>{content.button.label}</a>
        </div>
      )}
    </Slot>
  );
}
```

For more information, refer to the [`preferredLocale`](api/hooks/use-content#options-preferredlocale-prop) documentation.

## Context variables

Sometimes you need to provide additional information to personalize or segment your users.

For example, if you are working on a SaaS application, you may want to personalize the content based on the subscription plan, quota usage, features, or any other application-specific information. You can achieve this by passing any relevant information to the [`attributes`](api/hooks/use-content#options-attributes-prop) option:

**Hook — JavaScript**

```jsx
import {useContent} from '@croct/plug-react';

export function UpgradeBanner() {
  const {content} = useContent('upgrade-banner', {
    attributes: {plan: 'premium'},
  });

  return (
    <div>
      <strong>{content.title}</strong>
      <p>{content.subtitle}</p>
      <a href={content.button.url}>{content.button.label}</a>
    </div>
  );
}
```

**Hook — TypeScript**

```tsx
import type {ReactElement} from 'react';
import {useContent} from '@croct/plug-react';

export function UpgradeBanner(): ReactElement {
  const {content} = useContent('upgrade-banner', {
    attributes: {plan: 'premium'},
  });

  return (
    <div>
      <strong>{content.title}</strong>
      <p>{content.subtitle}</p>
      <a href={content.button.url}>{content.button.label}</a>
    </div>
  );
}
```

**Component — JavaScript**

```jsx
import {Slot} from '@croct/plug-react';

export function UpgradeBanner() {
  return (
    <Slot id="upgrade-banner" attributes={{plan: 'premium'}}>
      {({content}) => (
        <div>
          <strong>{content.title}</strong>
          <p>{content.subtitle}</p>
          <a href={content.button.url}>{content.button.label}</a>
        </div>
      )}
    </Slot>
  );
}
```

**Component — TypeScript**

```tsx
import type {ReactElement} from 'react';
import {Slot} from '@croct/plug-react';

export function UpgradeBanner(): ReactElement {
  return (
    <Slot id="upgrade-banner" attributes={{plan: 'premium'}}>
      {({content}) => (
        <div>
          <strong>{content.title}</strong>
          <p>{content.subtitle}</p>
          <a href={content.button.url}>{content.button.label}</a>
        </div>
      )}
    </Slot>
  );
}
```

These values are then accessible as custom attributes in the [context variable](/reference/cql/context#evaluation):

```cql
context's plan is "premium"
```

Keep in mind that the context has some constraints on the number of attributes and levels of nesting. For more information, please refer to the [`attributes`](api/hooks/use-content#options-attributes-prop) documentation.

## Explore

- [Slots](/explanation/slot): Learn how slots help you organize and personalize your content.
- [Fetch hook](api/hooks/use-content): Explore the hook documentation and available options.
