# Custom element quickstart

Write a Node subclass with @vidova/2d, implement animateIn and animate, then place it with component create and timeline_edit addClip.

This path assumes Vidova is running, Local MCP is on, and a project is open in the editor. Install steps are on the [MCP overview](/docs/mcp).

## 1. Write the class

The exported class name must match `componentName`. `animate()` is the timeline entry point. `animateIn()` is the intro.

```tsx
import { Node, NodeProps, Txt, signal, initial, colorSignal } from '@vidova/2d';
import {
  SignalValue,
  SimpleSignal,
  ColorSignal,
  PossibleColor,
  easeOutCubic,
  type ThreadGenerator,
} from '@vidova/core';

export interface HelloTitleProps extends NodeProps {
  label?: SignalValue<string>;
  textColor?: SignalValue<PossibleColor>;
  textSize?: SignalValue<number>;
}

export class HelloTitle extends Node {
  @initial('Hello')
  @signal()
  public declare readonly label: SimpleSignal<string, this>;

  @initial('#ffffff')
  @colorSignal()
  public declare readonly textColor: ColorSignal<this>;

  @initial(48)
  @signal()
  public declare readonly textSize: SimpleSignal<number, this>;

  public constructor(props?: HelloTitleProps) {
    super({ ...props });
    this.add(
      <Txt
        text={() => this.label()}
        fill={() => this.textColor()}
        fontSize={() => this.textSize()}
        fontFamily="Inter Variable"
        fontWeight={600}
      />,
    );
  }

  public *animateIn(duration: number = 0.4): ThreadGenerator {
    this.opacity(0);
    yield* this.opacity(1, duration, easeOutCubic);
  }

  public *animate(duration?: number): ThreadGenerator {
    yield* this.animateIn(duration ?? 0.4);
  }
}
```

Use the exact `fontFamily` string. `Inter Variable` is the default UI font. Code text uses `Fira Code Variable`.

## 2. Create the asset

Call [component](/docs/mcp/tools/component) with `action` `create`:

```json
{
  "action": "create",
  "name": "Hello title",
  "componentName": "HelloTitle",
  "code": "<the TSX above>"
}
```

The result includes `assetId`. If the tool reports `FAILED TO RENDER`, fix the class before you place it.

## 3. Place it on the timeline

Call [timeline_edit](/docs/mcp/tools/timeline_edit) with `action` `addClip`, `type` `component`, and that `assetId`. Never `type=text`.

## 4. Capture a preview

Call `preview` with `action` `captureFrame` at a timecode where the clip is visible. Confirm the text is on screen and fully inside the frame.

A catalog template is faster when one already covers the design. Use `createFromTemplate` instead of `create`. Full props and methods: [Authoring](/docs/custom-elements/authoring).
