The content-render subject is the main pipeline that converts content from Liquid, Markdown, and YAML into HTML. It handles template processing, Markdown parsing, custom Liquid tags, and unified (remark/rehype) transformations. This directory does not include React components.
This subject is responsible for:
- Rendering Liquid templates with context variables
- Converting Markdown to HTML with unified (remark/rehype)
- Custom Liquid tags for content and data references
- Code block syntax highlighting and headers
- Link rewriting (local paths, assets, anchors)
- Image transformations and wrapping
- Alert/note callout rendering
- Table accessibility improvements
- Text-only extraction for search indexing
Architecture & Key Assets
Key capabilities and their locations
- index.ts - renderContent(): Main entry point for content rendering
- liquid/engine.ts - Liquid engine with custom tag registration
- unified/processor.ts - Unified pipeline with remark/rehype plugins
- liquid/*.ts - Custom Liquid tags (data, ifversion, octicon, etc.)
- unified/*.ts - Content transformation plugins
import { renderContent } from '@/content-render'
const html = await renderContent(`
# Beep
{{ foo }}
`, {
foo: 'bar'
})
Creates:
<h1 id="beep"><a href="#beep">Beep</a></h1>
<p>bar</p>
renderContent(markdown, context = {}, options = {})
Render a string of markdown with optional context. Returns a Promise.
Liquid will look for includes in ${process.cwd()}/includes.
Options:
- fileName: File name for debugging purposes
- textOnly: Output text instead of HTML using cheerio (for search indexing)
The Liquid instance used internally for direct access.
Add a header to code blocks with the copy annotation:
```js copy
const copyMe = true
```
The un-highlighted text is available as button.js-btn-copy's data-clipboard-text attribute.
Data & External Dependencies
- Markdown content with Liquid templates
- Context object with variables and functions
- Data from data/ directory (reusables, variables, features)
- Content includes from includes/ directory
- LiquidJS - Template engine for Liquid processing
- unified/remark/rehype - Markdown to HTML transformation
- cheerio - HTML parsing for text-only mode
- highlight.js - Syntax highlighting for code blocks
- Custom plugins for GitHub-specific transformations
- Liquid rendering - Process Liquid tags and variables
- Markdown parsing - Convert to syntax tree (remark)
- Unified plugins - Apply transformations
- HTML generation - Convert to final HTML (rehype)
- Post-processing - Additional cleanup if needed
- src/frame - Page rendering uses renderContent
- src/data-directory - Data accessed via {% data %} tags
- src/versions - {% ifversion %} tag logic
- All content in content/ - Source of Markdown to render
- Includes in includes/ - Reusable Liquid includes
Current State & Next Steps
Custom tags implemented:
| Tag |
Purpose |
| {% data variables.product.product_name %} |
Access data variables |
| {% ifversion fpt %}...{% endif %} |
Conditional content by version |
| {% octicon "check" %} |
Render Octicons |
| {% indented_data_reference foo.bar spaces=2 %} |
Data reference with indentation |
| {% tool name %} |
Tool-specific content |
| {% prompt %} |
Command prompt styling |
See contributing/liquid-helpers.md for complete list.
Tags can be used in:
- Articles and TOCs (content/**/*.md)
- Include files (includes/*.html)
Tags expect language-agnostic hrefs or data paths:
{% data variables.product.product_name %}
{% ifversion ghes > 3.9 %}...{% endif %}
- Create TypeScript class in liquid/my-tag.ts and implement the rendering logic directly in the class (using inline HTML or template strings).
- Register in liquid/engine.ts:
import MyTag from './my-tag'
engine.registerTag('my_tag', MyTag)
- Add tests in tests/
- Document in contributing/liquid-helpers.md
See LiquidJS docs for tag API.
Plugins transform the Markdown AST:
- rewrite-local-links - Rewrites internal /en/... links
- rewrite-asset-urls - Handles /assets/... paths
- heading-links - Adds anchor links to headings
- alerts - Converts > [!NOTE] to styled alerts
- code-header - Adds copy buttons to code blocks
- And many more...
Adding new unified plugins
- Create plugin in unified/my-plugin.ts
- Add to processor in unified/processor.ts
- Add tests
- Consider impact on performance (plugins run on every render)
- Liquid rendering happens before Markdown parsing (can't use Markdown in Liquid output easily)
- Some transformations are performance-sensitive (cached where possible)
- Text-only mode used for search has different output than HTML mode
- Custom Liquid tags must be registered manually
Performance considerations
- Rendering is cached at the page level
- Liquid includes are resolved on every render
- Heavy transformations should be avoided in hot paths
- Use textOnly mode for search indexing (faster)