Skip to content

Components

Angular components inside Markdown: registration, children, and how a tag is told apart from HTML.

A tag in your Markdown can become a real Angular component. This is the centre of Blasdoc, so it is worth being precise about how a tag is recognised, how it is resolved, and what happens when it is not.

Using one

Registering

A component is only ever instantiated when you have registered it. Nothing in the content can reach anything else.

On a page, listed where it is used:

ts
@Component({
  selector: 'app-guide-page',
  templateMD,
  components: [AppButton, AppCallout],
})
export class GuidePage {}

Application-wide, when many documents share a component:

ts
provideBlasdoc({
  components: {
    'app-button': AppButton,
    'app-callout': AppCallout,
  },
});

The list form reads each component's own selector, so the name cannot drift from the class. The record form is there when you want a different name, or a lazy loader.

Children

Children are projected through <ng-content />, and they are Markdown:

md
<app-callout type="warning">
  This is **Markdown** inside a component, with a [link](/docs).
</app-callout>

Nesting works to any depth, and a component may span several Markdown blocks.

Self-closing

md
<app-spacer />

HTML is not a component

<div> stays a div. The rule is the tag name: a name containing a dash is a component, everything else is HTML.

A plain div, rendered as HTML.

You can change the rule:

ts
provideBlasdoc({
  parseOptions: {
    isComponentName: (name) => name.startsWith('app-'),
  },
});

Unknown components

The parser does not know your registry — it records a component node either way. The runtime is what reports the problem:

text
BLASDOC_UNKNOWN_COMPONENT  Unknown component "app-missing".

Nothing is instantiated on a guess. See Diagnostics.

The Markdown boundary

CommonMark only recognises HTML-shaped tags, so <app-callout type="warning"> arrives as a raw HTML block while <app-button [disabled]="x"> arrives as plain text — [disabled] is not a valid HTML attribute name. Blasdoc re-reads both through its own tokenizer, so the two spellings produce the same IR.

Two consequences worth knowing:

A paragraph is transparent to tags: it is only materialised once no tag crossed its boundary, so a component can span several blocks, and a paragraph holding nothing but tags is dropped rather than wrapping your component in a stray <p>.