Skip to content

CollierCZ/markdoc-svelte

Repository files navigation

markdoc-svelte

Preprocess Markdoc files for use in Svelte Kit sites.

Use

Add the .md extension and the markdoc preprocessor to your Svelte Kit configuration:

import { markdoc } from "markdoc-svelte";

/** @type {import('@sveltejs/kit').Config} */
const config = {
  extensions: [".svelte", ".md"],
  preprocess: [markdoc()],
};

Add custom schema

Markdoc allows you to configure various options for parsing:

To include these options in your preprocessing, pass them in the configuration. You can do this in two ways:

  • Pass each individually as an option.
  • Create a schema directory to include files that define each option for you. Include that directory in the markdown-svelte configuration.

In each case, imports happen before bundling, so import files as relative paths to JavaScript files with the extension. Use JavaScript files or run Node.js with a tool such as tsx to use TypeScript.

Functions

Use Markdoc functions to transform content. Include them as a file or directory in your Markdoc schema directory or in the markdoc-svelte configuration.

import { markdoc } from "markdoc-svelte";

const uppercase = {
  transform(parameters) {
    const string = parameters[0];

    return typeof string === "string" ? string.toUpperCase() : string;
  },
};

/** @type {import('@sveltejs/kit').Config} */
const config = {
  preprocess: [
    markdoc({
      functions: { uppercase },
    }),
  ],
};

Nodes

Use Markdoc nodes to customize how standard Markdown elements are rendered. Include them as a file or directory in your Markdoc schema directory or in the markdoc-svelte configuration.

Imports happen before bundling, so import files as relative paths to JavaScript files with the extension.

import { markdoc } from "markdoc-svelte";
import { link } from "./markdown/link.js";

/** @type {import('@sveltejs/kit').Config} */
const config = {
  preprocess: [
    markdoc({
      nodes: { link },
    }),
  ],
};

Partials

Use Markdoc partials to reuse blocks of content in various places. Include them in a directory in your Markdoc schema directory or define a partials directory as a relative path in the markdoc-svelte configuration.

import { markdoc } from "markdoc-svelte";

/** @type {import('@sveltejs/kit').Config} */
const config = {
  preprocess: [
    markdoc({
      partials: "./markdoc/partials",
    }),
  ],
};

Tags

Use Markdoc tags to extend Markdown elements to do more. Include them as a file or directory in your Markdoc schema directory or in the markdoc-svelte configuration.

Imports happen before bundling, so import files as relative paths to JavaScript files with the extension.

import { markdoc } from "markdoc-svelte";
import { button } from "./markdown/button.js";

/** @type {import('@sveltejs/kit').Config} */
const config = {
  preprocess: [
    markdoc({
      nodes: { button },
    }),
  ],
};

Variables

Use Markdoc variables to customize content during the build. Include them as a file in your Markdoc schema directory or in the markdoc-svelte configuration.

import { markdoc } from "markdoc-svelte";

/** @type {import('@sveltejs/kit').Config} */
const config = {
  preprocess: [
    markdoc({
      variables: { flags: { best_feature_flag: true } },
    }),
  ],
};

Code blocks

Markdoc tags are processed as the first thing. This allows you to do things like use Markdoc variables inside code blocks. But sometimes you want to include text like {% %} inside a code block.

To mark a single code block to not be processed for tags, add a attribute to the block:

```markdown {% process = false %}
Use variables in your code: `{% product_name %}`
```

To set this as the default option, create a custom fence node and set a different default (example).

Frontmatter

Frontmatter added as YAML is automatically parsed. So you could add frontmatter like the following:

---
title: A great page
---

With great content

You can then access it in your layouts:

<script lang="ts">
  let {
    children,
    title = '',
  } = $props()
</script>

<h1>{ title }</h1>

<!-- Article content -->
{@render children?.()}

And in your content:

---
title: Using the Next.js plugin
description: Integrate Markdoc into your Next.js app
---

# {% $frontmatter.title %}

Options

You can choose to customize how Markdoc files are processed.

Validation level

This preprocessor validates whether the Markdoc is valid. By default, it throws an error on files for errors at the error or critical level. To debug, you can set the level to a lower level and it stops the build for any errors at that level or above. Possible values in ascending order: debug, info, warning, error, critical

import { markdoc } from "markdoc-svelte";

/** @type {import('@sveltejs/kit').Config} */
const config = {
  extensions: [".markdoc", ".md"],
  preprocess: [
    markdoc({
      validationLevel: "info",
    }),
  ],
};

Extensions

By default, files ending in .markdoc and .md are preprocessed. To use other extensions, add them to the extensions array in the options:

import { markdoc } from "markdoc-svelte";

/** @type {import('@sveltejs/kit').Config} */
const config = {
  extensions: [".svelte", ".markdoc", ".md"],
  preprocess: [
    markdoc({
      extensions: [".svelte", ".markdoc", ".md"],
    }),
  ],
};

Layout

To give your processed Markdoc a layout, pass the path to the layout file:

import { markdoc } from "markdoc-svelte";

/** @type {import('@sveltejs/kit').Config} */
const config = {
  extensions: [".svelte", ".md"],
  preprocess: [
    markdoc({
      layout: "/path/to/layout.svelte",
    }),
  ],
};

Frontmatter in YAML format is automatically passed to your layout as props. The content is passed as children that can then be rendered.

<script lang="ts">
  let {
    children,
    title = '',
  } = $props()
</script>

<h1>{ title }</h1>

<!-- Article content -->
{@render children?.()}

Schema path

To define Markdoc options, you can use a directory that holds multiple options. You can define each option as a single file or a directory with an index.js file that exports the option. Except for partials, which is a directory holding Markdoc files.

Example structure:

markdoc
├── functions.js
├── nodes
│   ├── heading.js
│   ├── index.js
│   └── callout.js
├── partials
│   ├── content.markdoc
│   └── more-content.markdoc
├── tags.js
└── variables.js

By default, the preprocessor looks for your Markdoc schema definition in a ./markdoc directory at the app root. To use a different path, define the directory in the options as a relative path.

import { markdoc } from "markdoc-svelte";

/** @type {import('@sveltejs/kit').Config} */
const config = {
  preprocess: [
    markdoc({
      schema: "./path/to/schema/directory",
    }),
  ],
};

Markdoc config options

In addition to the option to include Markdoc configuration as a single directory, you can pass each option individually: