# Content rendering

Learn how to fetch and render content for your slots.

This guide provides practical examples of how to use the Nuxt 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/composables/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/composables/use-content) composable or the [`<Slot>`](api/components/slot-content) component, depending on whether you prefer an imperative or declarative approach. Both use Nuxt's `useAsyncData` under the hood, so the content is fetched on the server during SSR and hydrated on the client.

**Composable — JavaScript**

```vue
<script setup>
const {data} = await useContent('home-hero');
</script>

<template>
  <div v-if="data">
    <strong>{{ data.content.title }}</strong>
    <p>{{ data.content.subtitle }}</p>
    <a :href="data.content.button.link">{{ data.content.button.label }}</a>
  </div>
</template>
```

**Composable — TypeScript**

```vue
<script setup lang="ts">
const {data} = await useContent('home-hero');
</script>

<template>
  <div v-if="data">
    <strong>{{ data.content.title }}</strong>
    <p>{{ data.content.subtitle }}</p>
    <a :href="data.content.button.link">{{ data.content.button.label }}</a>
  </div>
</template>
```

**Component — JavaScript**

```vue
<template>
  <Slot id="home-hero" v-slot="{ content }">
    <div>
      <strong>{{ content.title }}</strong>
      <p>{{ content.subtitle }}</p>
      <a :href="content.button.link">{{ content.button.label }}</a>
    </div>
  </Slot>
</template>
```

**Component — TypeScript**

```vue
<template>
  <Slot id="home-hero" v-slot="{ content }">
    <div>
      <strong>{{ content.title }}</strong>
      <p>{{ content.subtitle }}</p>
      <a :href="content.button.link">{{ content.button.label }}</a>
    </div>
  </Slot>
</template>
```

> **Example: How to render content on the client side?**
>
> For client-side rendering, you can use the composables and components from `@croct/plug-nuxt/csr`:
>
> **Composable — JavaScript**
>
> ```vue
> <script setup>
> import {useContent} from '@croct/plug-nuxt/csr';
>
> const {data} = useContent('home-hero');
> </script>
>
> <template>
>   <div v-if="data">
>     <strong>{{ data.title }}</strong>
>     <p>{{ data.subtitle }}</p>
>     <a :href="data.button.link">{{ data.button.label }}</a>
>   </div>
> </template>
> ```
>
> **Composable — TypeScript**
>
> ```vue
> <script setup lang="ts">
> import {useContent} from '@croct/plug-nuxt/csr';
>
> const {data} = useContent('home-hero');
> </script>
>
> <template>
>   <div v-if="data">
>     <strong>{{ data.title }}</strong>
>     <p>{{ data.subtitle }}</p>
>     <a :href="data.button.link">{{ data.button.label }}</a>
>   </div>
> </template>
> ```
>
> **Component — JavaScript**
>
> ```vue
> <script setup>
> import {Slot} from '@croct/plug-nuxt/csr';
> </script>
>
> <template>
>   <Slot id="home-hero" v-slot="{ content }">
>     <div>
>       <strong>{{ content.title }}</strong>
>       <p>{{ content.subtitle }}</p>
>       <a :href="content.button.link">{{ content.button.label }}</a>
>     </div>
>   </Slot>
> </template>
> ```
>
> **Component — TypeScript**
>
> ```vue
> <script setup lang="ts">
> import {Slot} from '@croct/plug-nuxt/csr';
> </script>
>
> <template>
>   <Slot id="home-hero" v-slot="{ content }">
>     <div>
>       <strong>{{ content.title }}</strong>
>       <p>{{ content.subtitle }}</p>
>       <a :href="content.button.link">{{ content.button.label }}</a>
>     </div>
>   </Slot>
> </template>
> ```

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

```ts
import type {SlotContent} from '@croct/plug-nuxt/types';

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

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

If you added the slot using the CLI, your application already has automatic [fallback content](/explanation/content/fallback-content). The CLI downloads default content that the SDK uses when a content request fails. You can optionally provide an explicit fallback to override it. See the [fallback hierarchy](/reference/cli/fallback-content#fallback-hierarchy) for how the SDK resolves content.

If you want to specify a custom fallback, pass it as an option:

**Composable — JavaScript**

```vue
<script setup>
const {data} = await useContent('home-hero', {
  fallback: {
    title: 'Welcome to Croct!',
    subtitle: 'The easiest way to personalize your application.',
    button: {
      label: 'Get started',
      link: '/signup',
    }
  },
});
</script>

<template>
  <div v-if="data">
    <strong>{{ data.content.title }}</strong>
    <p>{{ data.content.subtitle }}</p>
    <a :href="data.content.button.link">{{ data.content.button.label }}</a>
  </div>
</template>
```

**Composable — TypeScript**

```vue
<script setup lang="ts">
const {data} = await useContent('home-hero', {
  fallback: {
    title: 'Welcome to Croct!',
    subtitle: 'The easiest way to personalize your application.',
    button: {
      label: 'Get started',
      link: '/signup',
    }
  },
});
</script>

<template>
  <div v-if="data">
    <strong>{{ data.content.title }}</strong>
    <p>{{ data.content.subtitle }}</p>
    <a :href="data.content.button.link">{{ data.content.button.label }}</a>
  </div>
</template>
```

**Component — JavaScript**

```vue
<script setup>
const fallback = {
  title: 'Welcome to Croct!',
  subtitle: 'The easiest way to personalize your application.',
  button: {
    label: 'Get started',
    link: '/signup',
  },
};
</script>

<template>
  <Slot id="home-hero" :fallback="fallback" v-slot="{ content }">
    <div>
      <strong>{{ content.title }}</strong>
      <p>{{ content.subtitle }}</p>
      <a :href="content.button.link">{{ content.button.label }}</a>
    </div>
  </Slot>
</template>
```

**Component — TypeScript**

```vue
<script setup lang="ts">
const fallback = {
  title: 'Welcome to Croct!',
  subtitle: 'The easiest way to personalize your application.',
  button: {
    label: 'Get started',
    link: '/signup',
  },
};
</script>

<template>
  <Slot id="home-hero" :fallback="fallback" v-slot="{ content }">
    <div>
      <strong>{{ content.title }}</strong>
      <p>{{ content.subtitle }}</p>
      <a :href="content.button.link">{{ content.button.label }}</a>
    </div>
  </Slot>
</template>
```

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.

**Composable — JavaScript**

```vue
<script setup>
const {data} = await useContent('home-hero@2');
</script>

<template>
  <div v-if="data">
    <strong>{{ data.content.title }}</strong>
    <p>{{ data.content.subtitle }}</p>
    <a :href="data.content.button.link">{{ data.content.button.label }}</a>
  </div>
</template>
```

**Composable — TypeScript**

```vue
<script setup lang="ts">
const {data} = await useContent('home-hero@2');
</script>

<template>
  <div v-if="data">
    <strong>{{ data.content.title }}</strong>
    <p>{{ data.content.subtitle }}</p>
    <a :href="data.content.button.link">{{ data.content.button.label }}</a>
  </div>
</template>
```

**Component — JavaScript**

```vue
<template>
  <Slot id="home-hero@2" v-slot="{ content }">
    <div>
      <strong>{{ content.title }}</strong>
      <p>{{ content.subtitle }}</p>
      <a :href="content.button.link">{{ content.button.label }}</a>
    </div>
  </Slot>
</template>
```

**Component — TypeScript**

```vue
<template>
  <Slot id="home-hero@2" v-slot="{ content }">
    <div>
      <strong>{{ content.title }}</strong>
      <p>{{ content.subtitle }}</p>
      <a :href="content.button.link">{{ content.button.label }}</a>
    </div>
  </Slot>
</template>
```

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

## Localization

To support multiple locales, you can use the [`preferredLocale`](api/composables/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.

If you are using [`@nuxtjs/i18n`](https://i18n.nuxtjs.org), the SDK automatically uses the locale from the i18n module. If you are not using i18n, or if there is no content available in that locale, the SDK will fall back to the [default locale of your workspace](https://app.croct.com/redirect/organizations/-organization-/workspaces/-workspace-/settings).

You can also set a [default locale](api/configuration#defaultpreferredlocale-prop) in your module configuration, or specify a different locale as needed:

**Composable — JavaScript**

```vue
<script setup>
const {data} = await useContent('home-hero', {
  preferredLocale: 'en-ca',
});
</script>

<template>
  <div v-if="data">
    <strong>{{ data.content.title }}</strong>
    <p>{{ data.content.subtitle }}</p>
    <a :href="data.content.button.link">{{ data.content.button.label }}</a>
  </div>
</template>
```

**Composable — TypeScript**

```vue
<script setup lang="ts">
const {data} = await useContent('home-hero', {
  preferredLocale: 'en-ca',
});
</script>

<template>
  <div v-if="data">
    <strong>{{ data.content.title }}</strong>
    <p>{{ data.content.subtitle }}</p>
    <a :href="data.content.button.link">{{ data.content.button.label }}</a>
  </div>
</template>
```

**Component — JavaScript**

```vue
<template>
  <Slot id="home-hero" preferred-locale="en-ca" v-slot="{ content }">
    <div>
      <strong>{{ content.title }}</strong>
      <p>{{ content.subtitle }}</p>
      <a :href="content.button.link">{{ content.button.label }}</a>
    </div>
  </Slot>
</template>
```

**Component — TypeScript**

```vue
<template>
  <Slot id="home-hero" preferred-locale="en-ca" v-slot="{ content }">
    <div>
      <strong>{{ content.title }}</strong>
      <p>{{ content.subtitle }}</p>
      <a :href="content.button.link">{{ content.button.label }}</a>
    </div>
  </Slot>
</template>
```

For more information, refer to the [`preferredLocale`](api/composables/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/composables/use-content#options-attributes-prop) option:

**Composable — JavaScript**

```vue
<script setup>
const {data} = await useContent('upgrade-banner', {
  context: {attributes: {plan: 'premium'}},
});
</script>

<template>
  <div v-if="data">
    <strong>{{ data.content.title }}</strong>
    <p>{{ data.content.subtitle }}</p>
    <a :href="data.content.button.url">{{ data.content.button.label }}</a>
  </div>
</template>
```

**Composable — TypeScript**

```vue
<script setup lang="ts">
const {data} = await useContent('upgrade-banner', {
  context: {attributes: {plan: 'premium'}},
});
</script>

<template>
  <div v-if="data">
    <strong>{{ data.content.title }}</strong>
    <p>{{ data.content.subtitle }}</p>
    <a :href="data.content.button.url">{{ data.content.button.label }}</a>
  </div>
</template>
```

**Component — JavaScript**

```vue
<template>
  <Slot id="upgrade-banner" :attributes="{plan: 'premium'}" v-slot="{ content }">
    <div>
      <strong>{{ content.title }}</strong>
      <p>{{ content.subtitle }}</p>
      <a :href="content.button.url">{{ content.button.label }}</a>
    </div>
  </Slot>
</template>
```

**Component — TypeScript**

```vue
<template>
  <Slot id="upgrade-banner" :attributes="{plan: 'premium'}" v-slot="{ content }">
    <div>
      <strong>{{ content.title }}</strong>
      <p>{{ content.subtitle }}</p>
      <a :href="content.button.url">{{ content.button.label }}</a>
    </div>
  </Slot>
</template>
```

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/composables/use-content#options-attributes-prop) documentation.

## Explore

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