Shortcodes vs Components for Docs Authors
Documentation writers need more than Markdown: callouts, tabbed code samples, figures with captions, version badges, step lists, card grids. Eleventy provides them as shortcodes — functions called from Nunjucks or Liquid inside Markdown. Astro provides them as components — .astro (or framework) files used from MDX with JSX syntax. From an engineer's point of view the difference is architectural. From a writer's point of view it is about syntax, error messages and how easily a page breaks.
This guide implements the same six authoring elements both ways, has four technical writers use each for a week, and compares mistakes, build failures, output weight and maintenance. It is part of Astro vs Eleventy for Documentation Sites, and the underlying format choice is examined in MDX vs Markdoc for Docs Content.
Prerequisites
- An Eleventy site using Markdown with Nunjucks preprocessing (the default), or an Astro site with MDX enabled.
- A list of the authoring elements writers actually request — gather it from existing pages rather than imagining it.
- A contributor guide where the vocabulary will be documented.
The Same Callout, Two Ways
Eleventy paired shortcode:
// eleventy.config.js
eleventyConfig.addPairedShortcode('callout', (content, type = 'note', title = '') => {
const md = markdownLib.render(content);
return `<aside class="callout callout-${type}" role="note">${title ? `<p class="callout-title">${title}</p>` : ''}${md}</aside>`;
});
{% callout "warning", "Rotate keys first" %}
Revoke the old key **after** the new one is deployed.
{% endcallout %}
Astro component used in MDX:
---
// src/components/Callout.astro
const { type = 'note', title } = Astro.props;
---
<aside class={`callout callout-${type}`} role="note">
{title && <p class="callout-title">{title}</p>}
<slot />
</aside>
import Callout from '../../components/Callout.astro';
<Callout type="warning" title="Rotate keys first">
Revoke the old key **after** the new one is deployed.
</Callout>
Both produce identical HTML. The authoring differences are visible even here: the MDX page needs an import line, and the component syntax is HTML-like, which some writers find familiar and others find easy to mistype.
The Writer Study
Four technical writers each spent a week adding and editing pages on both sites, using the same six elements: callout, tabs, code block with filename, figure, version badge and step list. Every failed build and every rendering mistake caught in review was logged.
| Measure (per 100 page edits) | Eleventy shortcodes | Astro components in MDX |
|---|---|---|
| Failed builds | 1.4 | 4.6 |
| Rendering mistakes caught in review | 2.1 | 1.2 |
| Median time to fix a failed build | 2 min | 9 min |
| Writers preferring it at the end | 3 of 4 | 1 of 4 |
< or { is code to an MDX parser.The pattern is consistent with other measurements on this site. Components produced fewer rendering mistakes because props are named and typed — a tab without a label fails the build rather than rendering blank — but MDX's parser turned ordinary prose into failed builds. Eleventy's errors were fewer and easier to understand ("unclosed tag callout on line 42"). The writer who preferred Astro had a front-end background and liked editor autocompletion for component props.
Output Weight and JavaScript
Rendered statically, both approaches produce the same HTML, and neither adds JavaScript. The difference appears with interactive elements such as tabs. In Eleventy, tabs need a small hand-written script (about 0.9 KB) included on pages that use them, or pure-CSS tabs with radio inputs. In Astro, a React <Tabs> component with client:load shipped 44 KB of framework runtime to every page with tabs; rewriting it as an .astro component with a 0.8 KB inline script removed that entirely. The lesson generalises: components make adding JavaScript effortless, so review every client: directive, as described in Astro Islands vs Full Hydration Performance.
Editor Support and Previews
Writers spend their time in an editor and a preview, so tooling around the mechanism matters as much as the mechanism. MDX has strong editor support: the VS Code MDX extension highlights JSX, and TypeScript-typed props give autocompletion for component attributes, which is why the one front-end-minded writer preferred it. Nunjucks shortcodes get syntax highlighting from the Nunjucks extension but no argument hints; a snippets file with one entry per shortcode closes most of that gap in ten minutes of setup.
Previews differ more. Eleventy's --serve with incremental rebuilds showed a shortcode change in under a second; Astro's dev server showed MDX changes as fast, but an MDX parse error replaced the whole page with an error overlay, which writers found alarming even when the fix was one character. Both teams added the same safety net: a pre-commit hook that builds only the changed pages and reports errors in plain language — "line 42: < in text, write it as < or put it in backticks" — before a pull request is ever opened. That hook cut failed CI builds on the Astro side by about half during the second week.
Keeping the Vocabulary Small
Whichever mechanism you choose, the long-term cost is the size of the vocabulary. Every shortcode or component is something writers must learn, reviewers must recognise, and a future migration must port — the migration cost is discussed in Porting Shortcodes and Includes Between Generators. Three rules keep it manageable:
- Add elements from repeated need, not anticipation. A new element needs three existing pages that would use it.
- Document each one with a copy-pasteable example on a single vocabulary page, and link it from the pull request template.
- Lint for unknown elements. A CI check that lists shortcode names or component imports used in content, and fails on any not in the documented set, stops one-off elements quietly becoming permanent.
The docs site in this study had accumulated 31 shortcodes over four years; an audit found 9 unused, 7 used on a single page and 4 near-duplicates. Consolidating to 11 cut the contributor guide by half and made the migration experiment above feasible in a week.
A Middle Path: Markdown Directives
Teams that want neither template tags nor JSX in prose have a third option: remark directives, the :::callout{type=warning} syntax supported by remark-directive in Astro and by markdown-it-container or markdown-it-attrs in Eleventy. Directives stay inside Markdown, never execute code, and cannot be broken by a stray < in prose, which removes the largest MDX failure category. They suit block elements such as callouts and figures well and are awkward for anything with nested structure, such as tabs with several code blocks. On the study site, moving callouts and figures to directives while keeping tabs as components cut MDX files from 38% of pages to 6%.
Pitfalls & Rollback
- Converting every
.mdto.mdx. Only pages that use components need MDX; plain pages stay plain and cannot hit MDX parse errors. - Shortcodes that return unescaped user input. A shortcode argument inserted into HTML must be escaped like any template variable.
- Hydrating static components. A component without interactivity should never have a
client:directive. - Undocumented vocabulary. An element nobody knows about is one somebody will reinvent.
- Rollback: both mechanisms are additive. Removing an element means replacing its usages, which the lint check lists exactly.
Conclusion
Shortcodes and components render the same HTML; they differ in how writers experience them. In a week-long study, Eleventy's paired shortcodes produced a third of the failed builds of Astro components in MDX, mostly because MDX parses prose as code, while components caught more rendering mistakes through typed props. For writer-heavy documentation, shortcodes (or Markdoc tags) are the gentler default; for engineer-authored docs with interactive elements, components earn their strictness. Either way, a small, documented, linted vocabulary matters more than the mechanism.
FAQ
What is the difference between a shortcode and a component?
An Eleventy shortcode is a function called from a template with arguments that returns an HTML string. An Astro component is a file with its own markup, styles and optional client-side script, used in MDX with JSX syntax. Shortcodes are simpler; components are more capable and can ship JavaScript.
Which is easier for writers?
In our study, writers made fewer mistakes with paired Nunjucks shortcodes in plain Markdown than with components in MDX, mainly because MDX treats stray angle brackets and braces in prose as code. With a small, documented vocabulary, both are learnable in an afternoon.
Do components add JavaScript to pages?
Only if they are hydrated with a client directive. An Astro component used without a directive renders to static HTML with no JavaScript, the same as a shortcode. The risk is that adding a directive is one word, so JavaScript can creep in unnoticed.
How many shortcodes or components should a docs site have?
As few as cover the real needs, typically eight to twelve: callouts, tabs, code with a title, figures, version badges, cards and one or two domain-specific ones. More than twenty usually means one-off layouts have become permanent vocabulary.
Related
- Parent: Astro vs Eleventy for Documentation Sites — the wider comparison.
- MDX vs Markdoc for Docs Content — the format question underneath this one.
- Content Collections vs Eleventy Data Cascade — validating the data components receive.
- Best SSG for Technical Writers Without Coding Experience — writer experience across generators.
- Editorial Checks with Vale in CI — linting prose alongside markup.