# Content rendering

Learn how to fetch and render content for your slots.

This guide provides practical examples of how to use the JavaScript 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 [`croct.fetch('home-hero@2')`](api/plug/fetch) 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 the [`fetch`](api/plug/fetch) method. This method receives the ID of the slot you want to fetch and returns a promise that resolves to the content.

You can retrieve the content for this slot in a component as follows:

**Basic fetch**

**JavaScript**

```js
import croct from '@croct/plug';

async function renderHero() {
  const {content} = await croct.fetch('home-hero');
}
```

**TypeScript**

```ts
import croct from '@croct/plug';

async function renderHero(): Promise<void> {
  const {content} = await croct.fetch('home-hero');
}
```

**HTML**

```html
<!DOCTYPE html>
<html>
<head>
  <title>My awesome application</title>
  <script src="https://cdn.croct.io/js/v1/lib/plug.js"></script>
  <script>croct.plug({appId: 'APPLICATION_ID'});</script>
</head>
<body>
  <script>
    (async () => {
      const {content} = await croct.fetch('home-hero');
    })();
  </script>
</body>
</html>
```

Once the promise is resolved, you can use the result to render the content in your application. The actual structure of the content depends on the [schema](/reference/content/schema/introduction) of the slot, but here is an example:

**Content response**

```json
{
"title": "The best personalization platform for developers",
"subtitle": "Best-in-class developer experience and enterprise-grade reliability.",
"image": {
  "url": "https://cdn.croct.io/devs.png",
  "alt": "A screenshot of an editor showing a code snippet."
},
"button": {
  "label": "See docs",
  "url": "https://docs.croct.com"
}
}
```

You can then use this content to render the hero section of your homepage:

**Rendering example**

**JavaScript**

```js
import croct from '@croct/plug';

async function renderHero() {
  const {content} = await croct.fetch('home-hero');

  const hero = document.querySelector('.hero');
  const heading = hero.querySelector('h1');
  const subtitle = hero.querySelector('.subtitle');
  const image = hero.querySelector('.image');
  const button = hero.querySelector('.button');

  heading.innerText = content.title;
  subtitle.innerText = content.subtitle;
  image.setAttribute('src', content.image.url);
  image.setAttribute('alt', content.image.alt);
  button.innerText = content.button.label;
  button.setAttribute('href', content.button.url);
}

renderHero();
```

**TypeScript**

```ts
import croct from '@croct/plug';

async function renderHero(): Promise<void> {
  const {content} = await croct.fetch('home-hero');

  const hero = document.querySelector('.hero')!;
  const heading = hero.querySelector('h1')!;
  const subtitle = hero.querySelector('.subtitle')!;
  const image = hero.querySelector('.image')!;
  const button = hero.querySelector('.button')!;

  heading.innerText = content.title;
  subtitle.innerText = content.subtitle;
  image.setAttribute('src', content.image.url);
  image.setAttribute('alt', content.image.alt);
  button.innerText = content.button.label;
  button.setAttribute('href', content.button.url);
}

renderHero();
```

**HTML**

```html
<!DOCTYPE html>
<html>
<head>
  <title>My awesome application</title>
  <script src="https://cdn.croct.io/js/v1/lib/plug.js"></script>
  <script>croct.plug({appId: 'APPLICATION_ID'});</script>
</head>
<body>
  <div class="hero">
    ✨ Loading dynamic content...
  </div>
  <script>
    (async () => {
      const {content} = await croct.fetch('home-hero');

      const hero = document.querySelector('.hero');
      const heading = document.createElement('h1');
      const subtitle = document.createElement('p');
      const image = document.createElement('img');
      const button = document.createElement('a');

      heading.innerText = content.title;
      subtitle.innerText = content.subtitle;
      image.setAttribute('src', content.image.url);
      image.setAttribute('alt', content.image.alt);
      button.innerText = content.button.label;
      button.setAttribute('href', content.button.url);

      hero.appendChild(heading);
      hero.appendChild(subtitle);
      hero.appendChild(image);
      hero.appendChild(button);
    })();
  </script>
</body>
</html>
```

This is just a simple example, but you can use any templating engine or framework to render the content in your application.

## 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 based on slot or component schemas:

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

type HeroContent = 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

> **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.

You can achieve this by catching potential errors and providing a fallback content in case of error:

**Fail-safe fetch**

**JavaScript**

```js
import croct from '@croct/plug';

async function renderHero() {
  const {content} = await croct.fetch('home-hero')
    .catch(() => ({
      content: {
        title: 'Welcome to Croct!',
        subtitle: 'The easiest way to personalize your application.',
        image: {
          url: 'https://cdn.croct.io/hero.png',
          alt: 'A screenshot of the Croct dashboard.',
        },
        button: {
          label: 'Get started',
          url: 'https://croct.com',
        },
      }
    }));

  const hero = document.querySelector('.hero');
  const heading = hero.querySelector('h1');
  const subtitle = hero.querySelector('.subtitle');
  const image = hero.querySelector('.image');
  const button = hero.querySelector('.button');

  heading.innerText = content.title;
  subtitle.innerText = content.subtitle;
  image.setAttribute('src', content.image.url);
  image.setAttribute('alt', content.image.alt);
  button.innerText = content.button.label;
  button.setAttribute('href', content.button.url);
}

renderHero();
```

**TypeScript**

```ts
import croct from '@croct/plug';

async function renderHero(): Promise<void> {
  const {content} = await croct.fetch('home-hero')
    .catch(() => ({
      content: {
        title: 'Welcome to Croct!',
        subtitle: 'The easiest way to personalize your application.',
        image: {
          url: 'https://cdn.croct.io/hero.png',
          alt: 'A screenshot of the Croct dashboard.',
        },
        button: {
          label: 'Get started',
          url: 'https://croct.com',
        },
      }
    }));

  const hero = document.querySelector('.hero')!;
  const heading = hero.querySelector('h1')!;
  const subtitle = hero.querySelector('.subtitle')!;
  const image = hero.querySelector('.image')!;
  const button = hero.querySelector('.button')!;

  heading.innerText = content.title;
  subtitle.innerText = content.subtitle;
  image.setAttribute('src', content.image.url);
  image.setAttribute('alt', content.image.alt);
  button.innerText = content.button.label;
  button.setAttribute('href', content.button.url);
}

renderHero();
```

**HTML**

```html
<!DOCTYPE html>
<html>
<head>
  <title>My awesome application</title>
  <script src="https://cdn.croct.io/js/v1/lib/plug.js"></script>
  <script>croct.plug({appId: 'APPLICATION_ID'});</script>
</head>
<body>
  <div class="hero">
    ✨ Loading dynamic content...
  </div>
  <script>
    (async () => {
      const {content} = await croct.fetch('home-hero')
        .catch(() => ({
          content: {
            title: 'Welcome to Croct!',
            subtitle: 'The easiest way to personalize your application.',
            image: {
              url: 'https://cdn.croct.io/hero.png',
              alt: 'A screenshot of the Croct dashboard.',
            },
            button: {
              label: 'Get started',
              url: 'https://croct.com',
            },
          },
        }));

      const hero = document.querySelector('.hero');
      const heading = document.createElement('h1');
      const subtitle = document.createElement('p');
      const image = document.createElement('img');
      const button = document.createElement('a');

      heading.innerText = content.title;
      subtitle.innerText = content.subtitle;
      image.setAttribute('src', content.image.url);
      image.setAttribute('alt', content.image.alt);
      button.innerText = content.button.label;
      button.setAttribute('href', content.button.url);

      hero.appendChild(heading);
      hero.appendChild(subtitle);
      hero.appendChild(image);
      hero.appendChild(button);
  })();
  </script>
</body>
</html>
```

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

```ts
const {content} = await croct.fetch('home-hero@2');
```

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

## Localization

To support multiple locales, you can use the [`preferredLocale`](api/plug/fetch#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/plug/plug#configuration-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:

```ts
const {content} = await croct.fetch('home-hero', {
  preferredLocale: 'en-ca',
});
```

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

```ts
const {content} = await croct.fetch('upgrade-banner', {
  attributes: {plan: 'premium'},
});
```

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/plug/fetch#options-attributes-prop) documentation.

## Explore

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