Blueprints
Share plugins, layouts, components, and more between markdown files.
Blueprints are the heart of MDSX. They allow you to define reusable layouts, custom components, and specialized plugin configurations for different types of content. Think of them as templates that determine how your markdown files are rendered and styled.
What Are Blueprints?
A blueprint is a combination of:
- Layout Component - A Svelte component that wraps your markdown content
- Custom Components - Override default HTML elements with your own components
- Plugin Configuration - Specific remark/rehype plugins for that content type
- Metadata Handling - Access to frontmatter data in your layout
Blueprint Configuration
Blueprints are configured in the blueprints
property of your MDSXConfig
. You can define multiple blueprints for different content types.
import { defineConfig } from 'mdsx';
import rehypeSlug from 'rehype-slug';
import remarkGfm from 'remark-gfm';
import remarkCapitalize from 'remark-capitalize';
import rehypePrettyCode from 'rehype-pretty-code';
import rehypeToc from 'rehype-toc';
export const mdsxConfig = defineConfig({
extensions: ['.md'],
remarkPlugins: [remarkGfm],
rehypePlugins: [rehypeSlug],
blueprints: {
default: {
rehypePlugins: [rehypePrettyCode],
path: 'src/lib/blueprints/default/blueprint.svelte',
},
blog: {
remarkPlugins: [remarkCapitalize],
rehypePlugins: [rehypeToc],
path: 'src/lib/blueprints/blog/blueprint.svelte',
},
},
});
In this example:
default
- Used for general content with syntax highlightingblog
- Used for blog posts with table of contents and capitalized headings
Warning
The default
blueprint is required when using blueprints. This blueprint will be used for any markdown files that don't specify a blueprint in their frontmatter.
Plugin Execution Order
The plugins specified in the remarkPlugins
and rehypePlugins
properties of your blueprint configuration will only be applied to markdown files that use that blueprint.
Note
The plugins specified in the top-level remarkPlugins
and rehypePlugins
properties of your MDSXConfig
will be used for all markdown files, regardless of which blueprint they use. The blueprint-specific plugins will be executed after the top-level plugins.
Creating a Blueprint Component
The blueprint component acts as a wrapper for your markdown content. It receives the markdown content as a slot and can access frontmatter metadata as props.
Basic Blueprint Component
<script lang="ts">
let {
metadata,
children,
}: {
metadata: Record<string, unknown>;
children: any;
} = $props();
</script>
<article class="prose">
{@render children?.()}
</article>
Advanced Blueprint with Metadata
<script lang="ts">
let { metadata, children } = $props<{
metadata: {
title: string;
description: string;
author: string;
date: string;
coverImage?: string;
};
children: any;
}>();
</script>
<article class="blog-post">
{#if metadata.coverImage}
<img src={metadata.coverImage} alt={metadata.title} class="cover-image" />
{/if}
<header>
<h1>{metadata.title}</h1>
<p class="description">{metadata.description}</p>
<div class="meta">
<span>By {metadata.author}</span>
<time datetime={metadata.date}>{metadata.date}</time>
</div>
</header>
<div class="content">
{@render children?.()}
</div>
</article>
Tip
It's a good idea to use a common set of frontmatter properties across the markdown files that use a blueprint. This will make it easier to handle the metadata in your blueprint component and provide better type safety.
Custom Components
Blueprints can export custom components that override the default HTML elements when your markdown is rendered. This is one of the most powerful features of blueprints.
Exporting Custom Components
<script lang="ts" context="module">
export { default as h1 } from '$lib/components/h1.svelte';
export { default as h2 } from '$lib/components/h2.svelte';
export { default as p } from '$lib/components/p.svelte';
export { default as a } from '$lib/components/a.svelte';
export { default as code } from '$lib/components/code.svelte';
</script>
{@render children?.()}
Check out the Custom Components documentation for more information about custom components.
Using Blueprints
Once you have your blueprints defined, you can use them by specifying the blueprint
property in the frontmatter of your markdown files.
Default Blueprint
The default
blueprint is used automatically for any markdown files that don't specify a blueprint.
---
title: My Page
description: A simple page
---
# My Page
This content will use the default blueprint.
Named Blueprints
Specify a named blueprint to use a different layout and configuration.
---
title: My Blog Post
description: An interesting blog post
author: John Doe
date: 2024-01-15
blueprint: blog
---
# My Blog Post
This content will use the blog blueprint with its custom layout and plugins.
Opting Out of Blueprints
You can opt out of using any blueprint by setting blueprint: false
.
---
title: Standalone Page
blueprint: false
---
# Standalone Page
This content will be rendered without any blueprint wrapper.
Real-World Examples
Documentation Site Blueprint
<script lang="ts" module>
export { default as h1 } from '$lib/components/docs/h1.svelte';
export { default as h2 } from '$lib/components/docs/h2.svelte';
export { default as code } from '$lib/components/docs/code.svelte';
</script>
<script lang="ts">
import type { Snippet } from 'svelte';
let {
metadata,
children,
}: {
metadata: {
title: string;
description: string;
section: string;
};
children: Snippet;
} = $props();
</script>
<div class="docs-layout">
<nav class="sidebar">
<!-- Navigation based on section -->
</nav>
<main class="content">
<header>
<h1>{metadata.title}</h1>
<p>{metadata.description}</p>
</header>
{@render children?.()}
</main>
</div>
E-commerce Product Blueprint
<script lang="ts">
let {
metadata,
children,
}: {
metadata: {
title: string;
price: number;
images: string[];
sku: string;
};
children: Snippet;
} = $props();
</script>
<div class="product-page">
<div class="product-gallery">
{#each metadata.images as image}
<img src={image} alt={metadata.title} />
{/each}
</div>
<div class="product-info">
<h1>{metadata.title}</h1>
<p class="price">${metadata.price}</p>
<p class="sku">SKU: {metadata.sku}</p>
<div class="description">
{@render children?.()}
</div>
<button class="add-to-cart">Add to Cart</button>
</div>
</div>
Best Practices
Consistent Frontmatter
Use consistent frontmatter properties across files that use the same blueprint:
---
title: Required
description: Required
author: Optional
date: Optional
tags: Optional
---
Component Organization
Organize your custom components logically. For blueprint-specific components, co-locate them with their blueprint:
src/lib/
├── blueprints/
│ ├── default/
│ │ ├── blueprint.svelte
│ │ ├── h1.svelte
│ │ ├── h2.svelte
│ │ ├── p.svelte
│ │ └── a.svelte
│ ├── blog/
│ │ ├── blueprint.svelte
│ │ ├── h1.svelte
│ │ ├── blockquote.svelte
│ │ └── code.svelte
│ └── docs/
│ ├── blueprint.svelte
│ ├── h1.svelte
│ ├── h2.svelte
│ └── code.svelte
└── components/
└── shared/
├── external-link-icon.svelte
└── table.svelte
This approach keeps blueprint-specific components close to their blueprint definition, making it easier to maintain and understand the relationship between blueprints and their custom components.
Plugin Strategy
- Use global plugins for features needed across all content
- Use blueprint-specific plugins for specialized functionality
Next Steps
Now that you understand blueprints, explore these related topics:
- Frontmatter - Learn how to work with metadata in your blueprints
- External Links - See a complete example of custom components
- API Reference - Explore all blueprint configuration options
- Syntax Highlighting - Add code highlighting to your blueprints