# Create your first slot

Set up a slot and render it in your application.

In this tutorial, you will create a component, set up a slot, and render its content in your application. By the end, your content will be managed from the admin app, so anyone on the team can update it without touching code.

## Prerequisites

Before you start, make sure you have:

- A [Croct account](https://app.croct.com/signup) with a workspace and application set up.
- The [Croct SDK](/reference/sdk) installed in your project.

If you have not installed the SDK yet, follow the guide for your framework and come back here once the setup is done:

- [JavaScript](/reference/sdk/javascript/integration)
- [React](/reference/sdk/react/integration)
- [Next.js](/reference/sdk/nextjs/integration)
- [Hydrogen](/reference/sdk/hydrogen/integration)
- [Vue](/reference/sdk/vue/integration)
- [Nuxt](/reference/sdk/nuxt/integration)
- [PHP](/reference/sdk/php/integration)
- [Symfony](/reference/sdk/symfony/integration)
- [Laravel](/reference/sdk/laravel/integration)
- [Drupal](/reference/sdk/drupal/integration)
- [Storyblok](/reference/sdk/storyblok/integration)

## Create the component

A [component](/explanation/component) defines the structure of your content. Let's create a hero component with a title, subtitle, and a call-to-action button.

1. Open the [components page](https://app.croct.com/redirect/organizations/-organization-/workspaces/-workspace-/components) in the admin app.

2. Click **New component** and set the ID to `home-hero`.

3. Define the following attributes:

   | Attribute      | Type       | Required |
   | -------------- | ---------- | -------- |
   | `title`        | Plain text | Yes      |
   | `subtitle`     | Plain text | Yes      |
   | `button`       | Structure  | Yes      |
   | `button.label` | Plain text | Yes      |
   | `button.link`  | URL        | Yes      |

4. Save the component.

![Component](/assets/immersion/tutorials/get-started/content-management/component.png)

## Create the slot

A [slot](/explanation/slot) is a placeholder in your application where content is rendered. Let's create one for the hero section of your homepage.

1. Open to the [slots page](https://app.croct.com/redirect/organizations/-organization-/workspaces/-workspace-/slots) in the admin app.

2. Click **New slot**, set the ID to `home-hero`, and associate it with the `home-hero` component you just created.

3. Fill in the [default content](/explanation/content/slot-default-content), which is what users see when an active experience does not impact them.

   | Attribute      | Value                           |
   | -------------- | ------------------------------- |
   | `title`        | Learn more about our platform   |
   | `subtitle`     | The easiest way to get started. |
   | `button.label` | Get started                     |
   | `button.link`  | /signup                         |

4. Save the slot.

## Add the slot to your project

Pull the slot into your codebase so the SDK can fetch its content:

```sh
croct add slot home-hero
```

The CLI downloads the slot's [default content](/explanation/content/slot-default-content) and generates [type definitions](/reference/cli/type-generation) so your code has full autocomplete and type safety.

## Render the slot

Now that the slot is added, render it in your application. Below is an example of how to fetch and display the `home-hero` slot content:

**Plug JS — JavaScript**

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

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

  document.querySelector('#hero-title').textContent = content.title;
  document.querySelector('#hero-subtitle').textContent = content.subtitle;

  const button = document.querySelector('#hero-cta');

  button.textContent = content.button.label;
  button.href = content.button.link;
}

renderHero();
```

**Plug JS — TypeScript**

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

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

  document.querySelector<HTMLElement>('#hero-title')!.textContent = content.title;
  document.querySelector<HTMLElement>('#hero-subtitle')!.textContent = content.subtitle;

  const button = document.querySelector<HTMLAnchorElement>('#hero-cta')!;

  button.textContent = content.button.label;
  button.href = content.button.link;
}

renderHero();
```

**Plug React — JavaScript**

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

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

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

**Plug React — TypeScript**

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

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

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

**Plug Next — JavaScript**

```jsx
import {fetchContent} from '@croct/plug-next/server';

export default async function Home() {
  const content = await fetchContent('home-hero');

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

**Plug Next — TypeScript**

```tsx
import {fetchContent} from '@croct/plug-next/server';

export default async function Home() {
  const content = await fetchContent('home-hero');

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

**Plug Hydrogen — JavaScript**

```jsx
import {useLoaderData} from '@remix-run/react';
import {fetchContent} from '@croct/plug-hydrogen/server';

export async function loader({context}) {
  const {content} = await fetchContent('home-hero', {scope: context});

  return {hero: content};
}

export default function Index() {
  const {hero} = useLoaderData();

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

**Plug Hydrogen — TypeScript**

```tsx
import {useLoaderData} from '@remix-run/react';
import type {LoaderFunctionArgs} from '@shopify/remix-oxygen';
import {fetchContent} from '@croct/plug-hydrogen/server';

export async function loader({context}: LoaderFunctionArgs) {
  const {content} = await fetchContent('home-hero', {scope: context});

  return {hero: content};
}

export default function Index() {
  const {hero} = useLoaderData<typeof loader>();

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

**Plug Vue**

```vue
<script setup>
import {useContent} from '@croct/plug-vue'

const {data} = useContent('home-hero')
</script>

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

**Plug Nuxt**

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

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

**Plug PHP**

```php
<?php
use Croct\Plug\Croct;

$croct = Croct::fromDotenv();
$hero = $croct->fetchContent('home-hero')->getContent();
Croct::emitCookies();
?>
<div>
    <h1><?= $hero['title'] ?></h1>
    <p><?= $hero['subtitle'] ?></p>
    <a href="<?= $hero['button']['link'] ?>"><?= $hero['button']['label'] ?></a>
</div>
```

**Plug Symfony**

```php
<?php

namespace App\Controller;

use Croct\Plug\Plug;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

final class HomeController extends AbstractController
{
    #[Route('/', name: 'home')]
    public function index(Plug $croct): Response
    {
        $hero = $croct->fetchContent('home-hero')->getContent();

        return $this->render('home/index.html.twig', ['hero' => $hero]);
    }
}
```

**Plug Laravel**

```php
<?php

use Croct\Plug\Plug;
use Illuminate\Support\Facades\Route;

Route::get('/', function (Plug $croct) {
    $hero = $croct->fetchContent('home-hero')->getContent();

    return view('home', ['hero' => $hero]);
});
```

**Plug Drupal**

```php
<?php

namespace Drupal\my_module\Plugin\Block;

use Croct\Plug\Plug;
use Drupal\Core\Block\Attribute\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Symfony\Component\DependencyInjection\ContainerInterface;

#[Block(id: 'home_hero', admin_label: new TranslatableMarkup('Home hero'))]
final class HomeHeroBlock extends BlockBase implements ContainerFactoryPluginInterface
{
    private Plug $croct;

    public function __construct(array $configuration, string $pluginId, mixed $pluginDefinition, Plug $croct)
    {
        parent::__construct($configuration, $pluginId, $pluginDefinition);
        $this->croct = $croct;
    }

    public static function create(ContainerInterface $container, array $configuration, $pluginId, $pluginDefinition): self
    {
        return new self($configuration, $pluginId, $pluginDefinition, $container->get(Plug::class));
    }

    public function build(): array
    {
        $hero = $this->croct->fetchContent('home-hero')->getContent();

        return [
            '#type' => 'inline_template',
            '#template' => <<<'TWIG'
                <h1>{{ hero.title }}</h1>
                <p>{{ hero.subtitle }}</p>
                <a href="{{ hero.button.link }}">{{ hero.button.label }}</a>
                TWIG,
            '#context' => ['hero' => $hero],
            '#cache' => ['contexts' => ['session']],
        ];
    }
}
```

## Try it out

Your hero content is now dynamic and managed directly from the admin app. To see it in action:

1. Open your application and confirm the default content appears.

2. Open the `home-hero` slot again and edit the content.

3. Reload your application and check if you see the updated content.

![Slot default content](/assets/immersion/tutorials/get-started/content-management/slot-content.png)

Anyone on the team can now update this content from the admin app without a deploy.

## Explore

- [Component design](/immersion/guides/designing-components/best-practices): Learn the best practices for designing components.
- [Slots](/explanation/slot): Learn how slots make it easy to update content without code changes.
