How to create an Angular component with a different HTML tag than the selector 🏷
Tags: angular
Published: 2022-10-10 20:37:25

**Usually the HTML tag of an Angular component is it's selector. But it's possible to create a component with a different host HTML element. If you want to create a component statically (in the HTML template), you can use an _attribute [selector](https://angular.io/api/core/Directive#selector)_ defined in the `@Component` decorator. Or if you want to create a component dynamically via Angular API (e.g. [`createComponent()`](https://angular.io/api/core/createComponent) or [`ComponentFactory.create()`](https://angular.io/api/core/ComponentFactory#create)), you can pass a custom host DOM element as a parameter.**

### Creating a component _statically_

TLDR: See the [stackblitz: creating a component _statically_ with a custom HTML tag](https://stackblitz.com/edit/ng-create-component-statically-with-different-tag).

Define a component with an _attribute [selector](https://angular.io/api/core/Directive#selector)_ instead of _element selector_:
```ts
@Component({ 
  // Note the brakets around `my-component`, denoting an "attribute selector" instead of "element selector"
  selector: '[my-component]' 
  /* ... */
})
export class MyComponent { /* ... */ }
```

Then create this component with a custom HTML tag, by referencing it as an _attribute_:

```html
<article my-component></article>
```

### Creating a component _dynamically_

TLDR: See the [stackblitz: creating a component _dynamically_ with a custom HTML tag](https://stackblitz.com/edit/ng-create-component-dynamically-with-different-tag) .

Here, the selector doesn't matter. Can be even normal:

```ts
@Component({ 
  selector: 'my-component' 
  /* ... */
})
export class MyComponent { /* ... */ }
```

Then pass an existing DOM element as a host to the function for creating the component dynamically (e.g. to `createComponent()` from `@angular/core`):

```ts
import {
  createComponent,
  /* ... */
} from '@angular/core';

@Component(/*...*/)
export class ParentComponent {
  constructor(
    protected injector: Injector,
    protected environmentInjector: EnvironmentInjector,
    protected viewContainerRef: ViewContainerRef
  ) {}

  ngOnInit() {
    // create a fresh element, detached from DOM
    const hostElement = document.createElement('article');

    // create MyComponent, passing the `hostElement` param
    const component = createComponent(MyComponent, {
      hostElement, // <-----------------------------------
      environmentInjector: this.environmentInjector,
      elementInjector: this.injector,
    });

    // insert this component into the `ViewContainerRef` of the parent component
    this.viewContainerRef.insert(component.hostView);
  }
```

Note: The function [`createComponent()`](https://angular.io/api/core/createComponent) was added only in Angular 14.2 (see [CHANGELOG](https://github.com/angular/angular/blob/main/CHANGELOG.md#1420-2022-08-25)). The same feature is available also in [`ComponentFactory`](https://v14.angular.io/api/core/ComponentFactory#create) (which is [deprecated](https://angular.io/guide/deprecations) at the moment of writing).

See a similar example usage with `ComponentFactory`:
```ts
constructor(
    protected injector: Injector,
    protected componentFactoryResolver: ComponentFactoryResolver,
    protected viewContainerRef: ViewContainerRef
  ) {}

  ngOnInit() {
    // create a fresh element, detached from DOM
    const hostElement = document.createElement('article');

    const componentFactory =
      this.componentFactoryResolver.resolveComponentFactory(MyComponent);
    // create MyComponent, passing the `hostElement` param
    const component = componentFactory.create(
      this.injector,
      undefined,
      hostElement // <-----------------------------------
    );
    this.viewContainerRef.insert(component.hostView);
  }
```
Here you can find also the [working example on stackblitz  using `ComponentFactory`](https://stackblitz.com/edit/ng-create-component-dynamically-with-different-tag-facto).