Markdown
<wa-markdown>
Renders markdown content in plain ol' HTML.
The markdown component turns raw markdown into rendered HTML using the Marked library. Indentation is handled automatically. You can nest your markdown at any depth to match the surrounding HTML structure and the common leading whitespace will be stripped before parsing.
<wa-markdown> <script type="text/markdown"> ## Getting Started Here's a quick overview with **bold**, *italic*, and `inline code`. - Install the package - Import the component - Start writing markdown </script> </wa-markdown>
Since content is rendered client-side, it won't be visible to search engine crawlers or available before JavaScript loads. This makes it a poor fit for SEO-critical content like landing pages and blog posts. It's best suited for prototyping, dashboards, admin panels, and other contexts where search indexing isn't a concern.
Do not use this component with unsanitized user input. Markdown is parsed as-is without sanitization, so rendering untrusted content can lead to XSS vulnerabilities.
Examples
Jump to heading
Providing content
Jump to heading
Markdown must go inside a <script type="text/markdown"> element, which must be a direct child of the markdown component. (The script is required to prevent the browser from parsing the content.) The rendered output is placed in the light DOM where it inherits your page's styles.
<wa-markdown> <script type="text/markdown"> Markdown with `<angle brackets>` and **formatting** that won't get parsed as HTML. </script> </wa-markdown>
The Marked library is used under the hood to render markdown. Marked supports GitHub Flavored Markdown (GFM) and the CommonMark specification. This includes headings, bold, italic, links, images, lists, blockquotes, code blocks, tables, task lists, strike-through, and auto-links. For a full breakdown of supported syntax, see the Marked documentation.
Whitespace normalization
Jump to heading
Indentation inside the script is automatically normalized before the markdown parser sees it. This lets you indent your content to match the surrounding HTML without it being treated as a code block. The normalization process:
- Converts leading tabs to spaces based on the configured tab stop width (default: 4)
- Trims blank lines from the start and end
- Determines the smallest indentation shared by all non-empty lines
- Removes that common prefix from every line
This means you can write markdown at any indentation level and it will render correctly. Even if the content is indented by 8 spaces to match the HTML structure, the output renders as though it had no extra indentation at all.
<wa-markdown> <script type="text/markdown"> ## Deeply Indented Even though this content is heavily indented in the source, the shared whitespace is stripped before parsing. Lines with extra indentation beyond the common prefix are preserved, like this code block. </script> </wa-markdown>
For tab-indented source files, adjust the tab stop width with the tab-size attribute.
<wa-markdown tab-size="2"> <script type="text/markdown"> ... </script> </wa-markdown>
Formatting features
Jump to heading
All standard markdown formatting supported by Marked is available, including headings, lists, blockquotes, code blocks, links, and images.
<wa-markdown> <script type="text/markdown"> ## Feature Overview Steps to set up your project: 1. Clone the repository 2. Install dependencies 3. Run the dev server A relevant quote: > "Simplicity is the ultimate sophistication." — Leonardo da Vinci And an autolink: <https://developer.mozilla.org> </script> </wa-markdown>
Configuring Marked
Jump to heading
All <wa-markdown> instances share a single Marked instance. You can access it through any <wa-markdown> element's marked property. After making changes, call WaMarkdown.updateAll() to re-render every connected instance.
<wa-markdown id="markdown__config"> <script type="text/markdown"> Visit https://developer.mozilla.org for documentation. </script> </wa-markdown> <script type="module"> await customElements.whenDefined('wa-markdown'); const md = document.getElementById('markdown__config'); await new Promise(requestAnimationFrame); // Customize the link renderer to open links in a new tab const renderer = { link({ href, text }) { return `<a href="${href}" target="_blank" rel="noopener">${text}</a>`; } }; md.marked.use({ renderer }); md.renderMarkdown(); </script>
The Marked instance is shared across all <wa-markdown> elements. If you want every instance on the page to pick up the new configuration, call WaMarkdown.updateAll() instead of renderMarkdown() on a single element.
Writing a custom Marked plugin
Jump to heading
Custom Marked extensions can be applied through any element's marked property. The example below adds support for ==highlight== syntax, wrapping matched text in <mark> tags.
<wa-markdown id="markdown__plugin"> <script type="text/markdown"> This text has a ==highlighted phrase== in the middle. </script> </wa-markdown> <script type="module"> await customElements.whenDefined('wa-markdown'); const md = document.getElementById('markdown__plugin'); const highlight = { extensions: [{ name: 'highlight', level: 'inline', start(src) { return src.indexOf('=='); }, tokenizer(src) { const match = src.match(/^==([^=]+)==/); if (match) { return { type: 'highlight', raw: match[0], text: match[1] }; } }, renderer(token) { return `<mark>${token.text}</mark>`; } }] }; md.marked.use(highlight); md.renderMarkdown(); </script>
Updating content dynamically
Jump to heading
The component parses and renders automatically when the script element is first slotted in. It does not watch for subsequent changes to the script's content. To re-render after modifying the source, update the script's textContent and call renderMarkdown().
<div id="markdown__dynamic"> <wa-markdown id="markdown__dynamic-md"> <script type="text/markdown"> Click the button to swap this content out. </script> </wa-markdown> <br> <wa-button>Update content</wa-button> </div> <script type="module"> await customElements.whenDefined('wa-markdown'); const md = document.getElementById('markdown__dynamic-md'); const button = document.querySelector('#markdown__dynamic wa-button'); const script = md.querySelector('script[type="text/markdown"]'); button.addEventListener('click', () => { script.textContent = '## Fresh Content\n\nThis was **swapped in** by calling `renderMarkdown()`.'; md.renderMarkdown(); }); </script>
Importing
Jump to heading
Autoloading components via projects is the recommended way to import components. If you prefer to do it manually, use one of the following code snippets.
Let your project code do the work! Sign up for free to use a project with your very own CDN — it's the fastest and easiest way to use Web Awesome.
To manually import this component from NPM, use the following code.
import '@awesome.me/webawesome/dist/components/markdown/markdown.js';
To manually import this component from React, use the following code.
import WaMarkdown from '@awesome.me/webawesome/dist/react/markdown';
Attributes & Properties
Jump to heading
Learn more about attributes and properties.
| Name | Description | Reflects | |
|---|---|---|---|
css |
One or more CSSResultGroup to include in the component's shadow root. Host styles are automatically prepended.
Type
CSSResultGroup | undefinedDefault
styles |
||
marked |
A reference to the shared Marked instance for convenience. Equivalent to
WaMarkdown.getMarked().Type
Marked |
||
tabSizetab-size |
The tab stop width used when converting leading tabs to spaces during whitespace normalization.
Type
numberDefault
4 |
Methods
Jump to heading
Learn more about methods.
| Name | Description | Arguments |
|---|---|---|
getMarked() |
Returns the shared Marked instance used by all <wa-markdown> components. |
|
renderMarkdown() |
Reads the script content, normalizes whitespace, parses markdown, and injects the result. | |
updateAll() |
Re-renders all connected <wa-markdown> instances. Call this after changing the Marked configuration. |