# Type generation

Learn how the CLI generates type definitions for your project.

The CLI generates type definitions for your project based on your slot and component [schemas](/reference/content/schema/introduction). These definitions give your editor autocomplete and type checking when working with content.

## How it works

Every time you add, remove, install, update, or upgrade slots or components, the CLI fetches the corresponding schemas from your workspace and generates type definitions for the SDK in use.

Which versions get types depends on the [version specifier](configuration#version-specifiers) set for each entry in the [configuration file](configuration).

For example, in TypeScript projects this produces a `slots.d.ts` file that the CLI automatically registers in `tsconfig.json`, while in PHP projects it produces a `slots.stub` file that the SDK's PHPStan and Psalm plugins load automatically. Either way, calls like [`fetchContent('home-hero@2')`](/reference/sdk/php/api/core/plug/fetch-content) return the correct content type without any manual type annotations.

## Slots vs. components

Both slots and components get type definitions, but they serve different purposes.

**Slot types** define the content structure you receive when fetching content. They determine the shape of the object returned by functions like [`fetchContent`](/reference/sdk/nextjs/api/functions/fetch-content) or hooks like [`useContent`](/reference/sdk/nextjs/api/hooks/use-content), so your code knows exactly which properties are available.

**Component types** define reusable content structures that can appear inside slots. Because multiple slots can reference the same component, having a dedicated type for it lets you write shared rendering logic once and reuse it across different slots.

For example, the generated types let you work with content in a type-safe way:

**React**

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

// Type the full content of a slot
type HeroContent = SlotContent<'home-hero@1'>;

// Type a reusable component shared across slots
type CardProps = ComponentContent<'card@1'>;

function Card(props: CardProps) {
  return <div>{props.title}</div>;
}
```

**Next**

```tsx
import type {SlotContent, ComponentContent} from '@croct/plug-next';

// Type the full content of a slot
type HeroContent = SlotContent<'home-hero@1'>;

// Type a reusable component shared across slots
type CardProps = ComponentContent<'card@1'>;

function Card(props: CardProps) {
  return <div>{props.title}</div>;
}
```

**Vue**

```ts
import type {SlotContent, ComponentContent} from '@croct/plug-vue'

// Type the full content of a slot
type HeroContent = SlotContent<'home-hero@1'>

// Type a reusable component shared across slots
type CardProps = ComponentContent<'card@1'>
```

**Nuxt**

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

// Type the full content of a slot
type HeroContent = SlotContent<'home-hero@1'>

// Type a reusable component shared across slots
type CardProps = ComponentContent<'card@1'>
```

**JavaScript**

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

// Type the full content of a slot
type HeroContent = SlotContent<'home-hero@1'>;

// Type a reusable component shared across slots
type CardProps = ComponentContent<'card@1'>;
```

**PHP**

```php
<?php
/**
 * @phpstan-import-type HomeHero from \Croct\Content\Slots
 * @psalm-import-type HomeHero from \Croct\Content\Slots
 * @phpstan-import-type Card from \Croct\Content\Components
 * @psalm-import-type Card from \Croct\Content\Components
 */
final class HomePage
{
    /**
     * @param HomeHero $hero  The content of a slot.
     * @param Card $card      A reusable component shared across slots.
     */
    public function render(array $hero, array $card): string
    {
        return "<h1>{$hero['title']}</h1>";
    }
}
```

## Versioning

Types are generated per version, so your code keeps a stable structure as schemas evolve. Reference a specific version with the `@<version>` suffix on the slot or component ID, or omit it to follow the latest.

**React**

```ts
import type {SlotContent, VersionedSlotId, ComponentContent, VersionedComponentId} from '@croct/plug-react';

const banner: SlotContent<'home-banner@1'> = {...};
const slotId: VersionedSlotId = 'home-banner@1';
const card: ComponentContent<'card@1'> = {...};
const componentId: VersionedComponentId = 'card@latest';
```

**Next**

```ts
import type {SlotContent, VersionedSlotId, ComponentContent, VersionedComponentId} from '@croct/plug-next';

const banner: SlotContent<'home-banner@1'> = {...};
const slotId: VersionedSlotId = 'home-banner@1';
const card: ComponentContent<'card@1'> = {...};
const componentId: VersionedComponentId = 'card@latest';
```

**Vue**

```ts
import type {SlotContent, VersionedSlotId, ComponentContent, VersionedComponentId} from '@croct/plug-vue'

const banner: SlotContent<'home-banner@1'> = {...}
const slotId: VersionedSlotId = 'home-banner@1'
const card: ComponentContent<'card@1'> = {...}
const componentId: VersionedComponentId = 'card@latest'
```

**Nuxt**

```ts
import type {SlotContent, VersionedSlotId, ComponentContent, VersionedComponentId} from '@croct/plug-nuxt/types'

const banner: SlotContent<'home-banner@1'> = {...}
const slotId: VersionedSlotId = 'home-banner@1'
const card: ComponentContent<'card@1'> = {...}
const componentId: VersionedComponentId = 'card@latest'
```

**JavaScript**

```ts
import type {SlotContent, VersionedSlotId, ComponentContent, VersionedComponentId} from '@croct/plug';

const banner: SlotContent<'home-banner@1'> = {...};
const slotId: VersionedSlotId = 'home-banner@1';
const card: ComponentContent<'card@1'> = {...};
const componentId: VersionedComponentId = 'card@latest';
```

**PHP**

```php
<?php
/**
 * @phpstan-import-type HomeBannerV1 from \Croct\Content\Slots
 * @phpstan-import-type SlotId from \Croct\Content\MappedSlots
 * @phpstan-import-type CardV1 from \Croct\Content\Components
 * @phpstan-import-type ComponentId from \Croct\Content\MappedComponents
 *
 * @psalm-import-type HomeBannerV1 from \Croct\Content\Slots
 * @psalm-import-type SlotId from \Croct\Content\MappedSlots
 * @psalm-import-type CardV1 from \Croct\Content\Components
 * @psalm-import-type ComponentId from \Croct\Content\MappedComponents
 */
final class HomePage
{
    /** @var HomeBannerV1 */
    public array $banner;

    /** @var SlotId */
    public string $slotId = 'home-banner@1';

    /** @var CardV1 */
    public array $card;

    /** @var ComponentId */
    public string $componentId = 'card@latest';
}
```

## Synchronization

The [`install`](commands/install) command regenerates all type definitions. The CLI registers a postinstall hook so types stay in sync automatically whenever dependencies are installed.

You can also run [`update`](commands/update) to re-fetch schemas and regenerate types without changing any version specifiers. If you want to move to a newer major version, use [`upgrade`](commands/upgrade) instead, which updates the version specifiers in the configuration file and then regenerates types accordingly.

## Version control

The generated type definitions file does not need to be committed since the CLI regenerates it on install. However, committing it is recommended to avoid fetching definitions from the API at build time.

## Explore

- [Configuration file](/reference/cli/configuration): See the full format reference, including version specifiers.
- [Fallback content](/reference/cli/fallback-content): Learn how the CLI manages fallback content for your project.
- [Command reference](/reference/cli/commands): Browse the full list of CLI commands.
- [Add slot](/reference/cli/commands/add-slot): Learn how to add a slot to your project.
