A lightweight web component that fires MDX (Markdown + JSX) code reactively. Perfect for interactive documentation, dynamic content, and reactive interfaces.
Features:
- ⚡ Reactive: Responds to URL hash changes
- 📝 MDX Support: Full Markdown + JSX syntax
- 🎮 Three Trigger Modes: Hash-based, auto-fire attribute, or manual
- 🔒 Safe Execution: Isolated Shadow DOM execution
- 🎯 Vanilla JS: Framework-agnostic, zero dependencies* web component
- 📦 Lightweight: Only ~5KB minified, only @mdx-js/mdx dependency
*Only requires @mdx-js/mdx for compilation
npm install mdx-gun @mdx-js/mdx<mdx-gun fire>
# Hello World!
This content fires **automatically** when the page loads.
</mdx-gun><!-- Navigation links -->
<a href="#about">About</a>
<a href="#features">Features</a>
<!-- Sections triggered by hash -->
<mdx-gun id="about">
## About
Content shown when URL is #about
</mdx-gun>
<mdx-gun id="features">
## Features
Content shown when URL is #features
</mdx-gun><mdx-gun id="dynamic"></mdx-gun>
<script>
const gun = document.getElementById('dynamic');
const mdxCode = `
# Generated Content
This was created **dynamically** at ${new Date().toLocaleTimeString()}
`;
await gun.execute(mdxCode);
</script>| Attribute | Type | Description |
|---|---|---|
id |
string | Identifier for hash-based triggering |
fire |
boolean | Auto-execute on component load |
mdx-code |
string | MDX code to render |
Manually execute the MDX code.
const gun = document.querySelector('mdx-gun');
await gun.fire();Execute custom MDX code dynamically.
await gun.execute('# Dynamic Content\n\nCreated at ' + new Date());Inject custom React components for use in MDX.
const gun = document.querySelector('mdx-gun');
gun.setComponents({
Alert: ({ children }) => `<div class="alert">${children}</div>`,
Button: ({ label }) => `<button>${label}</button>`,
});
await gun.execute(`
<Alert>
<Button label="Click me" />
</Alert>
`);Provide variables accessible in the MDX context.
const gun = document.querySelector('mdx-gun');
gun.setScope({
username: 'Alice',
isDarkMode: true,
items: ['Item 1', 'Item 2', 'Item 3'],
});
await gun.execute(`
# Welcome, \${scope.username}!
Theme: \${scope.isDarkMode ? '🌙 Dark' : '☀️ Light'}
`);Fired when MDX code is successfully compiled and executed.
gun.addEventListener('mdx-executed', (event) => {
console.log('MDX executed with result:', event.detail.result);
console.log('Execution context:', event.detail.context);
});Fired when an error occurs during compilation or execution.
gun.addEventListener('mdx-error', (event) => {
console.error('MDX Error:', event.detail.error);
});<mdx-gun id="api-docs" fire>
# API Documentation
## Endpoints
- `GET /api/users` - List all users
- `POST /api/users` - Create a new user
- `PUT /api/users/:id` - Update user
- `DELETE /api/users/:id` - Delete user
## Example Response
\`\`\`json
{
"id": 1,
"name": "John Doe",
"email": "john@example.com"
}
\`\`\`
</mdx-gun><mdx-gun id="form-generator"></mdx-gun>
<script>
const gun = document.getElementById('form-generator');
gun.setScope({
fields: [
{ name: 'Name', type: 'text' },
{ name: 'Email', type: 'email' },
{ name: 'Message', type: 'textarea' },
]
});
const mdxCode = `
# Contact Form
\${scope.fields.map(field =>
\`<label>\${field.name}</label>
<input type="\${field.type}" name="\${field.name.toLowerCase()}" />\`
).join('')}
<button type="submit">Send</button>
`;
await gun.execute(mdxCode);
</script><mdx-gun fire id="counter"></mdx-gun>
<script>
const gun = document.getElementById('counter');
let count = 0;
gun.setScope({
count: count,
increment: () => {
count++;
renderCounter();
},
});
async function renderCounter() {
const mdxCode = `
# Counter: ${count}
<button onclick="window.incrementCounter()">+1</button>
Current value: **${count}**
`;
await gun.execute(mdxCode);
}
window.incrementCounter = gun.scope.increment;
renderCounter();
</script>- 📚 Interactive Docs: Create dynamic documentation that responds to navigation
- 🎓 Educational Content: Build interactive tutorials with executable examples
- 🎨 Design Systems: Showcase components and patterns dynamically
- 📊 Dashboards: Render dynamic content based on user interactions
- 🔍 Live Previews: Preview markdown/MDX in real-time
- 🌐 In-App Documentation: Embed help content that updates dynamically
npm install
npm run dev # Start dev server
npm run build # Build for production
npm run test # Run tests
npm run preview # Preview production buildmdx-gun/
├── src/
│ ├── index.ts # Main web component
│ └── index.test.ts # Tests
├── examples/
│ └── index.html # Demo page
├── dist/ # Built files (generated)
├── vite.config.ts # Build configuration
├── tsconfig.json # TypeScript config
└── package.json # Dependencies
- Chrome/Chromium: ✅
- Firefox: ✅
- Safari: ✅
- Edge: ✅
- Requires: ES2020, Web Components, Shadow DOM
- MDX compilation happens once and is cached
- Use
setScope()for dynamic data updates - Avoid re-rendering large components frequently
- Consider lazy-loading for large MDX files
- MDX code is evaluated in a controlled context
- Always validate user-provided MDX content
- Custom components should be carefully reviewed
- Never execute untrusted MDX code in high-security contexts
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new features
- Submit a pull request
MIT © 2026
This project brings the power of MDX to interactive web experiences, combining:
- Markdown's simplicity
- JSX's expressiveness
- Web Components' encapsulation
- Reactive paradigms for dynamic content
Perfect for developers who love writing in markdown but need the power of JavaScript!
Did you find this useful? Leave a ⭐ on GitHub!