Contextual Components

Pass Components Around as First-Class Values

Contextual Components

The component helper binds a component reference that you can yield, store, or render later — the building block of compound components.

4 min read Level 3/5 #ember#components#patterns
What you'll learn
  • Yield a component reference using the component helper
  • Render the yielded reference in the parent
  • Bind args at the yield site so the parent does less work

A contextual component is a component you pass around as a value. The component helper takes a name and optional bound args, and returns a component reference your parent can render. This is how compound components like tabs, menus, and listboxes are built.

Yielding A Component

{{! app/components/list.hbs }}
<ul>
  {{yield (hash
    item=(component "list-item")
    header=(component "list-header" theme=@theme)
  )}}
</ul>

The parent destructures and renders:

<List @theme="dark" as |api|>
  <api.header>Recent</api.header>
  <api.item @text="Apples" />
  <api.item @text="Bananas" />
</List>

Why Bind Args At The Yield Site

In the example above, theme was bound when list-header was yielded. The parent never has to know about @theme — it just renders <api.header> and the theme threads through automatically.

Compound Patterns

This unlocks tightly-coupled APIs without leaking state:

<Tabs as |t|>
  <t.list>
    <t.tab @id="one">One</t.tab>
    <t.tab @id="two">Two</t.tab>
  </t.list>
  <t.panel @id="one">First panel</t.panel>
  <t.panel @id="two">Second panel</t.panel>
</Tabs>

Internally, Tabs keeps a @tracked activeId and yields tab / panel components pre-wired with their selection logic.

Components As Values In JS

You can also store a component reference in JS — useful for dynamic renderers:

import { ensureSafeComponent } from '@embroider/util';
import Avatar from './avatar';

get profileComponent() {
  return ensureSafeComponent(Avatar, this);
}
Glimmer Internals →