Skip to content

Writing Components

There are two ways to include live Ember components in your documentation: inline code fences and file-based demos.

Known Limitations

By default, components are rendered without an Ember application container. Initializers and routing are not available. Service injection (@service) works when you call setupEmber with a services option in your theme setup.

Inline code fences

Write Ember components directly in your markdown using fenced code blocks. Add the live flag to make them interactive.

Template-only (GJS)

The simplest demo — just a <template> tag:

md
```gjs live
<template>
  <p>Hello from Ember!</p>
</template>
```

This renders the template inline in your page:

Class-based components

Import from @glimmer/component, @glimmer/tracking, and @ember/modifier to build stateful components:

md
```gjs live
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { on } from '@ember/modifier';

export default class Counter extends Component {
  @tracked count = 0;

  increment = () => {
    this.count++;
  };

  <template>
    <button type='button' {{on 'click' this.increment}}>
      Clicked:
      {{this.count}}
    </button>
  </template>
}
```

TypeScript (GTS)

Use gts instead of gjs for TypeScript components:

md
```gts live
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';

interface Signature {
  Args: { label?: string };
}

export default class Greeter extends Component<Signature> {
  @tracked name = 'world';

  <template>
    <p>Hello, {{this.name}}!</p>
  </template>
}
```

The plugin automatically strips type annotations via @babel/plugin-transform-typescript.

Code fence flags

SyntaxBehavior
```gjsStatic, syntax-highlighted code only
```gjs liveLive rendered component
```gjs live previewLive component with source code displayed below
```gts liveLive TypeScript component
```gts live previewLive TypeScript component with source code

Preview mode

Adding preview shows both the rendered output and the source code:

gjs
<template>
  <p style='color: seagreen; font-weight: bold;'>
    This component renders above its own source code.
  </p>
</template>

File-based demos

For larger components, keep them in separate .gjs / .gts files and reference them with the <CodePreview> component directly:

md
<CodePreview src="/demos/counter.gts" />

Place demo files in a demos/ directory (or anywhere under docs/). The path is relative to your VitePress root.

TIP

File-based demos are useful when a component is too large for a code fence, or when you want to share the same component across multiple pages.

Available imports

The plugin resolves these package namespaces automatically — no extra dependencies needed:

PackageCommon imports
@glimmer/componentComponent base class
@glimmer/trackingtracked, cached
@ember/modifieron modifier
@ember/helperfn, concat, get, hash
@ember/serviceservice decorator
@ember/ownergetOwner, setOwner
@ember/rendererrenderComponent (used by the Vue wrapper)

Any @ember/* or @glimmer/* import is resolved from ember-source's ESM packages automatically.

Custom packages

You can use any npm package installed in your project — just import it in your component code and Vite will resolve it from node_modules:

md
```gjs live
import { TrackedArray } from 'tracked-built-ins';
import Component from '@glimmer/component';
import { on } from '@ember/modifier';

export default class List extends Component {
  items = new TrackedArray(['hello', 'world']);

  add = () => {
    this.items.push('item ' + this.items.length);
  };

  <template>
    <ul>{{#each this.items as |item|}}<li>{{item}}</li>{{/each}}</ul>
    <button type='button' {{on 'click' this.add}}>Add</button>
  </template>
}
```

For packages that need special resolution (e.g., mapping a bare specifier to a local file), use the resolve option.

For addons that ship custom Babel plugins, use the babelPlugins option.

Styling components

Inline styles work as expected inside <template> tags. You can also use standard CSS approaches:

WARNING

<style> blocks inside templates are injected globally. Use unique class names to avoid conflicts between demos.