Skip to main content

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.

mdsx.config.js
		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 highlighting
  • blog - Used for blog posts with table of contents and capitalized headings

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.

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

src/lib/blueprints/default/blueprint.svelte
		<script lang="ts">
	let {
		metadata,
		children,
	}: {
		metadata: Record<string, unknown>;
		children: any;
	} = $props();
</script>
 
<article class="prose">
	{@render children?.()}
</article>
	

Advanced Blueprint with Metadata

src/lib/blueprints/blog/blueprint.svelte
		<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>
	

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

src/lib/blueprints/default/blueprint.svelte
		<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.

src/content/page.md
		---
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.

src/content/blog-post.md
		---
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.

src/content/standalone.md
		---
title: Standalone Page
blueprint: false
---
 
# Standalone Page
 
This content will be rendered without any blueprint wrapper.
	

Real-World Examples

Documentation Site Blueprint

src/lib/blueprints/docs/blueprint.svelte
		<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

src/lib/blueprints/product/blueprint.svelte
		<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: