Skip to content

custom-elements

Parses custom HTML-like elements from the markdown stream and renders them as actual Web Components. Demonstrates the plugin system handling non-markdown syntax.

Details

PropertyValue
Namecustom-elements
Priority50
TypeBlock
FactorycustomElements(options?)

Syntax

The plugin recognizes two patterns in the stream:

Self-closing:

markdown
<md-clock />
<md-counter start="5" />

Content-bearing:

markdown
<md-plot color="blue">1,2,4,7,3</md-plot>

Only whitelisted tag names are processed. Unknown tags are treated as plain text.

Built-in Custom Elements

<md-clock />

Displays a live ticking clock. Self-contained with no attributes. Uses setInterval internally, cleaned up on disconnectedCallback.

markdown
<md-clock />

<md-plot>

Takes comma-separated numbers as content and renders an inline SVG bar chart.

markdown
<md-plot color="blue" width="200" height="100">1,2,4,7,3</md-plot>
AttributeTypeDefaultDescription
widthnumber200Chart width in pixels
heightnumber100Chart height in pixels
colorstring"steelblue"Bar fill color

<md-counter>

Displays a number with increment and decrement buttons.

markdown
<md-counter start="5" min="0" max="10" />
AttributeTypeDefaultDescription
startnumber0Initial value
minnumbernoneMinimum value
maxnumbernoneMaximum value

API

ts
import { customElements } from '@generative-dom/plugins';

const plugin = customElements({
  allowedTags: ['md-clock', 'md-plot', 'md-counter'],
});

Options

OptionTypeDefaultDescription
allowedTagsstring[]All built-in tagsWhitelist of allowed custom element tag names

Security

  • Only whitelisted tags are parsed. <evil-script /> renders as plain text.
  • Event handler attributes (onclick, onerror, etc.) are stripped.
  • The style attribute is blocked if it contains url() or expression().
  • Inner content for custom elements is raw -- markdown inside is not parsed.

Edge Cases

  • Malformed tags: <clock, <clock / >, < clock/> are treated as plain text
  • Unknown tags: <evil-script /> is rendered as plain text
  • Special characters in attributes: <md-plot color="red&blue"> -- attributes are set via setAttribute, so special characters are safe
  • Self-closing vs. content-bearing mismatch: The parser handles both forms for any registered element
  • Stream splits mid-tag: <md-cl + ock /> -- the buffer waits for the complete tag
  • Markdown in content: <md-plot>**not bold**</md-plot> -- inner content is treated as raw text, not parsed as markdown

Rendering

Each custom element is registered as a real Web Component via customElements.define(). The plugin creates instances of these components and sets their attributes and content from the parsed stream.