skip to content

Programmatic Creation

Creating components from code with ViewContainerRef.createComponent, the standalone createComponent and NgComponentOutlet. Interviewers ask how inputs get in and why ComponentFactoryResolver is gone.

part ofAngularoverview, primer and where to startread it →
on this pageshow

explore

questions

5

In Angular code, how do you create a component with ViewContainerRef.createComponent, and where does it end up in the DOM and view tree?

level: middleimportance: must knowfreq 58%

answer

  1. a container at a location
  2. next sibling of the anchor
  3. class in, ComponentRef out
  4. destroyed with its container
  5. standalone createComponent attaches nowhere

basics

~20 s

Inject or query a ViewContainerRef and call createComponent(ComponentClass, options). Angular inserts the new host view into that container, after its anchor element, and returns a ComponentRef for setting inputs and destroying it; the view is checked and destroyed with its container.

solid answer

~40 s

A `ViewContainerRef` is a location in Angular's view tree that can hold views. Get one by injecting it (the location of the current component or directive) or with `viewChild('slot', { read: ViewContainerRef })` on an `<ng-container #slot />` anchor. `vcr.createComponent(Widget, { bindings, index, injector })` instantiates the class, inserts its host view into the container (by default last, rendered after the anchor) and returns a `ComponentRef` with `instance`, `setInput()`, `location`, `hostView` and `destroy()`. Because it is part of the view tree, the component is checked with the surrounding view, and it is destroyed when you call `ref.destroy()`, `vcr.clear()`, or when the container's own view is destroyed. The standalone `createComponent(Widget, { environmentInjector, hostElement })` is different: it attaches to nothing, so you insert the host element, call `appRef.attachView(ref.hostView)` for change detection, and destroy it yourself.

code

ts · 25 lines
ts
import { Component, ViewContainerRef, input, inputBinding, signal, viewChild } from '@angular/core';

@Component({ selector: 'app-toast', template: `<p>{{ message() }}</p>` })
export class Toast {
  readonly message = input.required<string>();
}

@Component({
  selector: 'app-toast-stack',
  template: `<button type="button" (click)="add()">Add</button><ng-container #slot />`,
})
export class ToastStack {
  private readonly slot = viewChild.required('slot', { read: ViewContainerRef });
  private readonly count = signal(0);

  add(): void {
    this.count.update((n) => n + 1);
    const text = `Saved item #${this.count()}`;
    const ref = this.slot().createComponent(Toast, {
      index: 0, // newest first
      bindings: [inputBinding('message', () => text)],
    });
    setTimeout(() => ref.destroy(), 3000);
  }
}

go deeper

for a junior

Recall that ViewContainerRef.createComponent takes a component class and returns a ComponentRef, and that an ng-container anchor marks where it goes.

for a middle

Explain placement after the anchor, index ordering, the ComponentRef API, and that the view is checked and destroyed with its container.

for a senior

Pick between NgComponentOutlet, a view container and standalone createComponent by placement, events and lifetime, and manage overlays attached through ApplicationRef without leaking them.

for a principal

Define where runtime-created UI is allowed to live, such as one overlay service and container-based plugin slots, so lifetimes and injectors stay predictable.

## View containers Angular renders a tree of **views**. A **view container** is a slot in that tree that can hold any number of views created at runtime; `ViewContainerRef` is its handle. Every element can act as one, and there are two usual ways to get a reference: - `inject(ViewContainerRef)` in a component or directive gives the container at **its own** location; created components are placed as siblings after that host element. - `viewChild.required('slot', { read: ViewContainerRef })` on an `<ng-container #slot />` gives a container at a specific **anchor** in the template, which is the tidier choice because `ng-container` renders no element. ## Creating the component `vcr.createComponent(ComponentClass, options?)` takes the component **class** (no factory, since v22 no factory exists) and optional settings: | Option | Purpose | |---|---| | `index` | position among the container's views; default is last | | `bindings` | `inputBinding`, `outputBinding`, `twoWayBinding` set up at creation (v20) | | `directives` | host directives to apply to the new component's host (v20) | | `injector` / `environmentInjector` | override where dependencies come from | | `projectableNodes` | `Node[][]`, one array per `<ng-content>` slot | | `onError` | callback for rendering errors (developer preview in 22.2) | It returns a `ComponentRef`: - `instance`: the component object. - `setInput(name, value)`: set an input by public name, marking the view dirty. - `location`: an `ElementRef` for the host element. - `hostView` / `changeDetectorRef`: the host view. - `destroy()` and `onDestroy(callback)`. ## Where it lands The new host element is inserted into the DOM **after the anchor**, in the order of the container's views. The docs' example: a component injecting its own `ViewContainerRef` and creating `LeafContent` produces `<inner-item>…</inner-item><leaf-content>…</leaf-content>`, a sibling, not a child. In the view tree it becomes a child of the container's declaring view, which has three consequences: 1. **Change detection.** It is checked when that part of the tree is checked, like any template child. 2. **Dependency injection.** By default it resolves dependencies from the container's location, so it sees services provided by ancestors there. 3. **Lifetime.** It is destroyed with the container's view; explicit cleanup is only needed to remove it earlier. ## Removing it - `ref.destroy()` removes and destroys one component. - `vcr.remove(index)` destroys the view at an index; `vcr.clear()` destroys all of them. - `vcr.detach(index)` removes a view without destroying it, for re-insertion elsewhere. All of these run the component's `ngOnDestroy` and `DestroyRef` callbacks. ## The standalone createComponent function `createComponent(ComponentClass, { environmentInjector, hostElement?, elementInjector?, bindings?, ... })` from `@angular/core` creates a component **without** attaching it anywhere. Use it for overlays, toasts and dialogs rendered outside the current view hierarchy, typically near the end of `<body>`: - Pass `hostElement` to render into an element you created; otherwise Angular creates a detached host element matching the selector. - Call `appRef.attachView(ref.hostView)` so the component takes part in change detection. - Append the host element to the DOM yourself. - Call `ref.destroy()` when done; destroying also detaches the view from `ApplicationRef`, and you remove the host element you appended. ## Common mistakes - **Querying the anchor too early.** A `viewChild` view-container query has no result until the view is created, so call `createComponent` from an event handler, `ngAfterViewInit` or later, not from the constructor. - **Injecting when you wanted "inside".** An injected `ViewContainerRef` places components after your host; use an `<ng-container>` anchor to place them within your template. - **Leaking standalone components.** Anything made with the standalone `createComponent` lives until you destroy it; tie `ref.destroy()` to the owner's `DestroyRef`. - **Assigning inputs on `instance`.** Use `setInput` or `bindings`, or an `OnPush` component will not refresh. ## Choosing | Need | Use | |---|---| | component chosen by template state, inputs only | `NgComponentOutlet` | | inline, ordered, part of this view, outputs or host directives | `ViewContainerRef.createComponent` | | outside the view hierarchy, e.g. a body-level overlay | standalone `createComponent` + `ApplicationRef` |

  • Why is the created component a sibling of the host that injected ViewContainerRef rather than a child?
    A component's injected `ViewContainerRef` represents its own location in the parent's view, and views in a container are rendered after the anchor node. The component's inner DOM belongs to its template, which Angular manages, so dynamic views go next to it. Use an `<ng-container #slot>` inside the template to render inside instead.
  • When would you use the standalone createComponent function instead of a ViewContainerRef?
    When the component must live outside the current view hierarchy, such as a popup appended to `document.body`. `createComponent` returns an unattached `ComponentRef`; you pass an `environmentInjector` and optionally a `hostElement`, attach the host view with `ApplicationRef.attachView`, insert the element, and destroy it yourself.

saying these in an interview costs you the question

  • createComponent needs a factory from ComponentFactoryResolver first.
  • A component created via an injected ViewContainerRef is rendered inside that host element.
  • Dynamically created components are never destroyed unless you call destroy() yourself.
  • The standalone createComponent function inserts the component at the caller's location.
  • Components from ViewContainerRef.createComponent are skipped by change detection.
open as a page

In an Angular template, how do you render a component whose class is only known at runtime, and pass it inputs?

level: juniorimportance: should knowfreq 44%

basics

~10 s

Use NgComponentOutlet: <ng-container *ngComponentOutlet="widgetType; inputs: widgetInputs" />. Angular creates the given component class in place, applies the inputs object with setInput on every check, and recreates the instance when the class changes.

open as a page

For an Angular component created from code, why use ComponentRef.setInput or inputBinding instead of assigning ref.instance fields, and how do outputBinding and twoWayBinding fit in?

level: middleimportance: should knowfreq 40%

basics

~20 s

Assigning ref.instance fields skips ngOnChanges and OnPush dirty marking and cannot set signal inputs. setInput sets an input by public name and marks the view dirty; inputBinding, outputBinding and twoWayBinding declare reactive template-like bindings at creation.

open as a page

An Angular dashboard must render widgets described by a JSON layout from the server; how do you design the dynamic creation, updates and teardown?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Map each JSON type to a component class through an explicit registry with lazy imports, create widgets in a ViewContainerRef with inputBinding/outputBinding, clear the container on relayout, guard against stale async loads, and validate input names with reflectComponentType.

open as a page

Why was ComponentFactoryResolver removed in Angular 22, and how do you migrate code that still uses it?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

Since Ivy, a component class carries its own compiled definition, so the factory lookup became redundant; ComponentFactoryResolver was deprecated in v13 and removed in v22. Pass the class straight to ViewContainerRef.createComponent or the standalone createComponent.

open as a page