# Dynamic

Source: https://signy.dev/documentation/1/templates/dynamic/

A tag or component chosen at runtime.

`Dynamic` renders whatever its `component` prop names: a tag such as `"h2"`, or a component class. When the choice changes, it removes the old one and builds the new one. Every other prop and the children go to what it renders.

Reach for it when the thing to render is data: a heading level from a setting, a view picked by a tab, a component looked up from a field type. For a fixed set of conditions, [Switch and Match](https://signy.dev/documentation/1/templates/switch-and-match/index.html.md) or [Show](https://signy.dev/documentation/1/templates/show/index.html.md) read better.

## Tags

Pass a tag name in `component`. Props become attributes and the children go inside the element. Wrap the choice in a function and it follows a signal.

```js
const { Component, Signal, application, html, Dynamic } = Signy;

class Heading extends Component {
  level = new Signal("h2");

  render() {
    return html`
      <div class="cluster">
        <button class="small" onClick=${() => (this.level.value = "h1")}>h1</button>
        <button class="small" onClick=${() => (this.level.value = "h2")}>h2</button>
        <button class="small" onClick=${() => (this.level.value = "h3")}>h3</button>
      </div>
      <${Dynamic} component=${() => this.level.value}>Heading</${Dynamic}>
    `;
  }
}

application(Heading).mount("#dynamic-heading");
```

## Components

A component class works the same way. The example keeps the current view in a signal and gives `Dynamic` an accessor that reads it. Each view is a class with its own state.

```js
const { Component, Signal, application, html, Dynamic } = Signy;

class Counter extends Component {
  count = new Signal(0);

  render() {
    return html`
      <button onClick=${() => this.count.set((c) => c + 1)}>
        Clicked ${() => this.count.value} times
      </button>
    `;
  }
}

class About extends Component {
  render() {
    return html`<p>Nothing to count here.</p>`;
  }
}

class Views extends Component {
  view = new Signal(Counter);

  render() {
    return html`
      <div class="cluster">
        <button class="small" onClick=${() => (this.view.value = Counter)}>Counter</button>
        <button class="small" onClick=${() => (this.view.value = About)}>About</button>
        <button class="small outlined" onClick=${() => (this.view.value = null)}>Nothing</button>
      </div>
      <${Dynamic} component=${() => this.view.value} />
    `;
  }
}

application(Views).mount("#dynamic-views");
```

Click the counter a few times, switch to About and come back: the count starts again at 0, because the old view was disposed and a new one built. Clicking *Counter* while it is already shown does nothing.

To store a class in a signal, assign `.value = Counter`. `signal.set(Counter)` would call the class as an updater function. If you want `set`, write `set(() => Counter)`.

## What component accepts

| You pass | What happens |
| --- | --- |
| `component="h2"` | That tag, fixed. |
| `component=${Counter}` | That component class, fixed. A class from another copy of Signy counts too, it is recognized by its `render` method. |
| `component=${() => view.value}` | Called to get the choice. It runs again when a signal it read changes, and returns a tag, a class, a function component, `null` or `undefined`. |
| `null`, `undefined` | Nothing is rendered. |
| Any other function | Called as an accessor, like the arrow function above. |
| Anything else, such as a node or a number | A warning in development, and nothing is rendered. |

This is the rule `Show` and `For` use: a function means "call me", and the call is tracked. Nothing else tells a fixed choice from a live one in an `html` template.

## Props and children

Everything except `component` and `children` is passed on. On a tag, a function prop is a binding, like anywhere in an `html` template. On a component, a prop that changes must be a function too, and the component calls it, as described in [props and children](https://signy.dev/documentation/1/components/props-and-children/index.html.md).

Here the select swaps the tag, the text field feeds the child, and a function prop keeps the `title` current. The children are made once and moved into each new tag.

```js
const { Component, Signal, application, html, Dynamic } = Signy;

class Preview extends Component {
  tag = new Signal("p");
  text = new Signal("Edit me");

  render() {
    return html`
      <div class="cluster">
        <select aria-label="Tag" bind:value=${this.tag}>
          <option value="p">p</option>
          <option value="h3">h3</option>
          <option value="blockquote">blockquote</option>
        </select>
        <input aria-label="Text" bind:value=${this.text} />
      </div>
      <${Dynamic} component=${() => this.tag.value} title=${() => "Text: " + this.text.value}>
        ${() => this.text.value}
      </${Dynamic}>
    `;
  }
}

application(Preview).mount("#dynamic-props");
```

Props of a component class are read when it is built. If `component` stays the same, a changed static prop does not rebuild it, so pass a function for anything that changes.

## When it swaps

- The effect tracks only `component` and the accessor it calls. What the rendered element or component reads does not rebuild it.
- It rebuilds only when the choice is a different value (`Object.is`). An accessor that returns the same class again keeps the instance, with its state.
- On a swap the old branch is disposed first: its effects stop, its `onCleanup` callbacks and `onUnmount` hooks run, and its nodes leave the page. Then the new one is built in the same place.
- Children come from the parent, so they are not disposed with the branch. They are moved into the new element, which keeps their listeners.

## Function components

A function that is not a component class is called as an accessor, so a function component must be returned from one. A bare `component=${Plain}` would be called with no arguments.

```js
function Note(props) {
  return html`<em>${props.children}</em>`;
}

html`<${Dynamic} component=${() => Note}>Careful</${Dynamic}>`;
```

In development, `Dynamic` warns when the choice is neither a tag name nor a function, for example when an accessor returns a node. It then renders nothing. A function component that reads `props` and was passed bare throws a `TypeError` instead, because it is called without any.

## In JSX

The same props, as a component. A prop that the compiler turns into a getter stays live on a tag. On a component class, pass a function for anything that changes.

```js
<Dynamic component={() => view.value} user={user} />

<Dynamic component="h2" title={label.value}>
  Heading
</Dynamic>
```

## Limits

- `component` and `children` are reserved. A target cannot receive props with those names.
- Event props (`onClick`) and `ref` are passed once. They are not bindings.
- A swap rebuilds everything inside, including state in the old component. Keep state that must survive in the parent or in a store.
- Transitions on enter and leave are not part of `Dynamic`.
