Components
Comark extends standard Markdown with component syntax, letting you embed custom UI directly in your content while keeping it readable.
Block
Block components use the ::component-name syntax and occupy their own line:
::component-name{prop1="value1" prop2="value2"}
Content inside the component
Can have **markdown** and other elements
::Here are some examples of block components:
::alert{type="info"}
This is an important message!
::::card{title="My Card"}
Card content with **markdown** support
::::divider
::Inline
Inline components use the :component-name syntax and can be placed within text:
Check out this :icon-star component in the middle of text.
Click the :button[Submit]{type="primary"} to continue.
The status is :badge[Active]{color="green"} right now.| Syntax | Description |
|---|---|
:icon-check | Standalone inline component |
:badge[New] | Inline component with content |
:badge[New]{color="blue"} | Inline component with content and properties |
:tooltip{text="Hover text"} | Inline component with properties only |
Properties
Components support two property syntaxes: inline attributes and YAML frontmatter.
Inline Attributes
Use the {...} syntax for simple properties:
::component{prop="value"}
<!-- Standard key-value pair -->
::
::component{bool}
<!-- Boolean property (becomes :bool="true" in AST) -->
::
::component{#custom-id}
<!-- ID attribute -->
::
::component{.class-name}
<!-- CSS class -->
::
::component{.class-one .class-two}
<!-- Multiple CSS classes -->
::
::component{obj='{"key": "value"}'}
<!-- Object/JSON value -->
::
::component{multiple="props" bool #id .class}
<!-- Multiple properties combined -->
::Block Props
For components with many properties, use YAML block syntax inside the component. Two styles are supported:
::component
```yaml [props]
title: My Component
type: info
count: 42
enabled: true
items:
- First item
- Second item
config:
theme: dark
mode: auto
```
Component content goes here
With full **markdown** support
::::component
---
title: My Component
type: info
count: 42
enabled: true
items:
- First item
- Second item
config:
theme: dark
mode: auto
---
Component content goes here
With full **markdown** support
::Key Rules
- Must be at the very beginning of the component content
- Enclosed by
---delimiters (frontmatter style) or```yaml [props]fences (codeblock style) - Merged with inline attributes (inline attributes take precedence)
- Full support for complex data structures
blockAttributesStyle option.Combine Both Syntaxes
Inline attributes and YAML props can be combined. Inline attributes take precedence:
::card{.featured}
```yaml [props]
title: Featured Article
author: Jane Doe
tags:
- markdown
- documentation
```
This combines inline class `.featured` with YAML props
::Use Cases
Configuration-heavy components:
::data-table
```yaml [props]
columns:
- name: Name
field: name
sortable: true
- name: Email
field: email
sortable: true
options:
striped: true
pageSize: 10
```
::API documentation:
::api-endpoint
```yaml [props]
method: POST
path: /api/users
parameters:
- name: username
type: string
required: true
- name: email
type: string
required: true
response:
status: 201
body:
id: string
username: string
```
::Data Binding
Props prefixed with : are JSON-parsed at render time. When the value isn't valid JSON, the renderer looks it up as a dot-path against an ambient render context, letting authors reference runtime data, frontmatter, meta, or the enclosing component's props without hardcoding them.
Scope
The render context exposes four namespaces:
| Namespace | Source |
|---|---|
frontmatter | The document's YAML frontmatter |
meta | Plugin-populated metadata on the parsed tree |
data | Runtime values passed via the renderer's data prop |
props | The enclosing component's own props |
Frontmatter
Reference any value declared in the document's frontmatter:
---
theAnswer: 42
user:
name: Ada
---
::question{:answer="frontmatter.theAnswer"}
::
Hello, :badge{:label="frontmatter.user.name"}Runtime data
Pass values from your app via the renderer's data prop and reference them under the data. namespace:
<Comark :markdown="content" :data="{ user: { name: 'Ada' } }" /><Comark markdown={content} data={{ user: { name: 'Ada' } }} /><Comark markdown={content} data={{ user: { name: 'Ada' } }} />Welcome, :badge{:label="data.user.name"}!Parent component props
Nested components can read the enclosing component's resolved props through the props. namespace. This is useful when a child should mirror something declared once on its parent:
::card{title="Hello" variant="primary"}
:::badge{:color="props.variant" :text="props.title"}
:::
::Resolution rules
- If the
:prefixedvalue is a valid JSON literal (e.g.5,true,"foo",{"a":1}), it's used as-is. - Otherwise, the value is treated as a dot-path and resolved against
{ frontmatter, meta, data, props }. - Unknown paths resolve to
undefined— the prop is passed asundefinedrather than the raw string. - Only props authored with the
:prefix participate in data binding. Plainprop="value"attributes are always passed as literal strings.
{{ path || default }} inline shorthand that resolves against the same render context.Slots
Block components support slots for passing structured content to components.
Default Slot
Content placed directly inside a component goes to the default slot — no prefix needed:
::alert{type="info"}
This content goes to the default slot.
::You can also write #default explicitly, which is useful when mixing it with named slots:
::card
#default
This is the **default** slot content.
#footer
Footer content here.
::Named Slots
::card
#header
## Card Title
#content
This is the main content of the card
#footer
Footer text here
::Combine with props
You can use both YAML block props and named slots in the same component:
::card{.featured}
```yaml [props]
variant: elevated
color: primary
actions:
- label: Read More
url: /article
- label: Share
icon: share
```
#header
## Article Title
*By Jane Doe*
#content
This is the main article content with **markdown** support.
#footer
Published on January 15, 2024
::::component line, before any slot definitions.Correct order:
::component{inline-attrs}
```yaml [props]
yaml: props
```
#slot-name
Slot content
::Incorrect order:
::component
#slot-name
Slot content
```yaml [props]
yaml: props <!-- This won't work -->
```
::Nested
Components can be nested within each other using additional colons:
::outer-component
Content in outer
:::inner-component{variant="compact"}
Content in inner
:::
More content in outer
::Deep nesting:
::level-1
:::level-2
::::level-3
Content
::::
:::
::This also works:
::level-1
::level-2
::level-3
Content
::
::
::