{"id":22416454,"url":"https://github.com/colliercz/markdoc-svelte","last_synced_at":"2025-07-09T06:08:45.706Z","repository":{"id":38441559,"uuid":"493926217","full_name":"CollierCZ/markdoc-svelte","owner":"CollierCZ","description":"A Svelte preprocessor for Markdoc","archived":false,"fork":false,"pushed_at":"2025-06-23T21:15:46.000Z","size":910,"stargazers_count":13,"open_issues_count":1,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-09T06:08:43.700Z","etag":null,"topics":["markdoc","markdown","preprocessor","svelte"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CollierCZ.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-05-19T04:59:51.000Z","updated_at":"2025-06-23T20:24:08.000Z","dependencies_parsed_at":"2025-06-01T18:26:29.878Z","dependency_job_id":"f632c8e2-269a-4f2e-9419-79f63eb9835f","html_url":"https://github.com/CollierCZ/markdoc-svelte","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/CollierCZ/markdoc-svelte","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CollierCZ%2Fmarkdoc-svelte","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CollierCZ%2Fmarkdoc-svelte/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CollierCZ%2Fmarkdoc-svelte/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CollierCZ%2Fmarkdoc-svelte/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CollierCZ","download_url":"https://codeload.github.com/CollierCZ/markdoc-svelte/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CollierCZ%2Fmarkdoc-svelte/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264403824,"owners_count":23602622,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["markdoc","markdown","preprocessor","svelte"],"created_at":"2024-12-05T15:16:20.103Z","updated_at":"2025-07-09T06:08:45.700Z","avatar_url":"https://github.com/CollierCZ.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# markdoc-svelte\n\n\u003cspan class=\"badge-npmversion\"\u003e\u003ca href=\"https://npmjs.org/package/markdoc-svelte\" title=\"View this project on NPM\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/markdoc-svelte.svg\" alt=\"NPM version\" /\u003e\u003c/a\u003e\u003c/span\u003e\n\nProcess Markdown and Markdoc files into Svelte components using [Markdoc](https://markdoc.dev/).\nUse Markdoc defaults out of the box or configure Markdoc schema to your needs.\n\n## Table of Contents\n\n- [Install](#install)\n- [Basic example](#basic-example)\n- [Frontmatter](#frontmatter)\n- [Customize Markdoc](#customize-markdoc)\n  - [Direct definitions](#direct-definitions)\n  - [Configuration folder](#configuration-folder)\n  - [Relative imports](#relative-imports)\n- [Preprocessor Options](#preprocessor-options)\n  - [Functions](#functions)\n  - [Nodes](#nodes)\n  - [Partials](#partials)\n  - [Tags](#tags)\n  - [Typographer](#typographer)\n  - [Validation level](#validation-level)\n  - [Variables](#variables)\n- [Advanced](#advanced)\n  - [Markdoc limitations](#markdoc-limitations)\n  - [@sveltejs/enhanced-img](#sveltejsenhanced-img)\n  - [Page table of contents](#page-table-of-contents)\n  - [Index page example](#index-page-example)\n\n## Install\n\nInstall `markdoc-svelte` in your SvelteKit project.\n\n```bash\nnpm install markdoc-svelte\n```\n\nAmend your SvelteKit config in `svelte.config.js` to:\n\n- Process files with the extensions you choose (such as `.mdoc` and `.md`).\n- Include the preprocessor.\n\n```javascript\nimport { markdocPreprocess } from \"markdoc-svelte\";\n\n/** @type {import('@sveltejs/kit').Config} */\nconst config = {\n  extensions: [\".svelte\", \".mdoc\", \".md\"],\n  preprocess: [markdocPreprocess()],\n};\n```\n\n## Basic example\n\nCreate a directory to hold your markdoc files: `src/lib/markdoc`\n\nIn that directory (at `src/lib/markdown/content.md`), add an example with basic Markdown syntax:\n\n```markdown\n---\ntitle: Hello World\n---\n\n# Hello World\n\nThis is a file that is processed by `markdoc-svelte`.\n\n![Alt text](/path/to/image.jpg)\n```\n\nDynamically import all files in the directory using a catchall route at `src/routes/[...catchall]/+page.ts`:\n\n```typescript\nimport { error } from \"@sveltejs/kit\";\nimport type { PageLoad } from \"./$types\";\nimport type { MarkdocModule } from \"markdoc-svelte\";\n\nexport const load: PageLoad = async ({ params }) =\u003e {\n  const slug = params.catchall;\n  try {\n    const page = (await import(`$lib/markdown/${slug}.md`)) as MarkdocModule;\n    return { page };\n  } catch {\n    throw error(404, `No corresponding file found for the slug \"${slug}\"`);\n  }\n};\n```\n\nRender the imported file as a Svelte component in a file at `src/routes/[...catchall]/+page.svelte`:\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n  import type { PageProps } from './$types';\n\n  let { data }: PageProps = $props();\n\u003c/script\u003e\n\n\u003csvelte:head\u003e\n  \u003ctitle\u003e{data.page.frontmatter?.title ?? 'Undefined title'}\u003c/title\u003e\n\u003c/svelte:head\u003e\n\n\u003cdata.page.default /\u003e\n```\n\nRun your dev server and visit `/content` to see the rendered file.\n\n## Frontmatter\n\nOptionally define YAML frontmatter in your file.\nThen use it in your content with the `$frontmatter` variable.\n\n```markdown\n---\ntitle: Why I switched to Markdoc\ndescription: What the benefits of Markdoc are and how to take advantage of them.\n---\n\n# {% $frontmatter.title %}\n```\n\nYou can also access the frontmatter in your Svelte page components.\nGet it from the data you defined in `+page.ts`:\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n  import type { PageProps } from './$types';\n\n  let { data }: PageProps = $props();\n\u003c/script\u003e\n\n\u003csvelte:head\u003e\n  \u003ctitle\u003e{data.page.frontmatter?.title ?? 'Default title'}\u003c/title\u003e\n  \u003cmeta name=\"description\" content={data.page.frontmatter?.description ?? 'Default description'} /\u003e\n\u003c/svelte:head\u003e\n\n\u003cdata.page.default /\u003e\n```\n\n## Customize Markdoc\n\nTo add additional features to the syntax of your files, customize your Markdoc schema.\nYou can add the following extensions:\n\n- [Nodes](#nodes)\n- [Tags](#tags)\n- [Variables](#variables)\n- [Functions](#functions)\n- [Partials](#partials)\n\nYou can customize schema in two ways:\n\n- **For a single extension** or simple extensions, pass directly to the preprocessor options.\n- **For multiple extensions at once** or more complex configurations,\n  create a configuration folder with schema definitions.\n\nFor each extension (such as `nodes`),\nschema definitions passed directly overwrite configuration from a folder.\n\n### Direct definitions\n\nTo define any of the extension points directly,\npass it to the preprocessor options as the name of the extension.\nFor example, to define a `$site.name` variable, pass the following:\n\n```javascript\nimport { markdocPreprocess } from \"markdoc-svelte\";\n\n/** @type {import('@sveltejs/kit').Config} */\nconst config = {\n  extensions: [\".svelte\", \".mdoc\"],\n  preprocess: [\n    markdocPreprocess({\n      variables: {\n        site: {\n          name: \"Markdoc Svelte\",\n        },\n      },\n    }),\n  ],\n};\n```\n\n### Configuration folder\n\nFor multiple extensions or more complex configurations, create a folder with your entire Markdoc schema.\n\nBy default, the preprocessor looks for a schema in the `./markdoc` and `./src/markdoc` directories.\n\nDefine each extension point as a single file\nor as a directory with an `index.ts` or `index.js` file that exports it.\nPartials must be a directory holding Markdoc files.\n\nAll extension points are optional.\n\nExample structure:\n\n```\nmarkdoc\n├── functions.ts\n├── nodes\n│   ├── heading.ts\n│   ├── index.ts\n│   └── callout.ts\n├── partials\n│   ├── content.mdoc\n│   └── more-content.mdoc\n├── tags.ts\n└── variables.ts\n```\n\nFor example, create custom nodes in `markdoc/nodes.ts`:\n\n```typescript\nimport type { Config } from \"markdoc-svelte\";\nimport { Markdoc } from \"markdoc-svelte\";\n\nconst nodes: Config[\"nodes\"] = {\n  image: {\n    render: \"EnhancedImage\",\n    attributes: {\n      ...Markdoc.nodes.image.attributes, // Include the default image attributes\n    },\n  },\n};\n\nexport default nodes;\n```\n\nOr create an index file to export all custom nodes from `markdoc/nodes/index.ts`\n(remember to use [relative imports](#relative-imports)):\n\n```typescript\nimport image from \"./image.ts\";\nimport link from \"./link.ts\";\nimport paragraph from \"./paragraph.ts\";\nimport type { Config } from \"markdoc-svelte\";\n\nconst nodes: Config[\"nodes\"] = {\n  image,\n  link,\n  paragraph,\n};\n\nexport default nodes;\n```\n\n### Relative imports\n\nYou can use relative imports to import definitions from either `.js` or `.ts` files.\nJust remember to include the file extension.\n\nFor example, if you define custom functions in `src/lib/functions.js`,\nadd them to your schema as follows:\n\n```javascript\nimport { markdocPreprocess } from \"markdoc-svelte\";\nimport functions from \"./src/lib/functions.js\";\n\n/** @type {import('@sveltejs/kit').Config} */\nconst config = {\n  preprocess: [\n    markdocPreprocess({\n      functions: functions,\n    }),\n  ],\n};\n```\n\n## Preprocessor Options\n\n| Option            | Type                | Default                          | Description                                                               |\n| ----------------- | ------------------- | -------------------------------- | ------------------------------------------------------------------------- |\n| `comments`        | boolean             | `true`                           | Enable [Markdown comments](https://spec.commonmark.org/0.30/#example-624) |\n| `components`      | string              | `\"$lib/components\"`              | Svelte components directory for custom nodes and tags                     |\n| `extensions`      | string[]            | `[\".mdoc\", \".md\"]`               | Files to process with Markdoc                                             |\n| `functions`       | Config['functions'] | -                                | [Functions config](#functions)                                            |\n| `layout`          | string              | -                                | Default layout for all processed Markdown files                           |\n| `linkify`         | boolean             | `false`                          | Auto-convert bare URLs to links                                           |\n| `nodes`           | Config['nodes']     | -                                | [Nodes config](#nodes)                                                    |\n| `partials`        | string              | -                                | [Partials](#partials) directory path                                      |\n| `schema`          | string              | `[\"./markdoc\", \"./src/markdoc\"]` | Schema directory path                                                     |\n| `tags`            | Config['tags']      | -                                | [Tags config](#tags)                                                      |\n| `typographer`     | boolean             | `false`                          | Enable [typography replacements](#typographer)                            |\n| `validationLevel` | ValidationLevel     | `\"error\"`                        | [Validation strictness level](#validation-level)                          |\n| `variables`       | Config['variables'] | -                                | [Variables config](#variables)                                            |\n\n### Functions\n\n[Functions](https://markdoc.dev/docs/functions) enable you to add custom utilities to Markdown\nso you can transform content and variables.\n\nFor example, you could add a function for transforming strings to uppercase to `markdoc/functions.ts`:\n\n```javascript\nimport type { Config } from \"markdoc-svelte\";\n\nconst functions: Config[\"functions\"] = {\n  uppercase: {\n    transform(parameters) {\n      const string = parameters[0];\n\n      return typeof string === \"string\" ? string.toUpperCase() : string;\n    },\n  },\n};\n\nexport default functions;\n```\n\nThen you can use the custom function in a Markdown file:\n\n```markdown\n---\ntitle: Hello World\n---\n\nThis is a {% uppercase(markdown) %} file that is processed by `markdoc-svelte`.\n```\n\n### Nodes\n\n[Nodes](https://markdoc.dev/docs/nodes) are elements built into Markdown from the CommonMark specification.\nCustomizing nodes enables you to change how existing elements from Markdown are rendered using Svelte components.\nThe components are automatically loaded from the components directory defined in your configuration.\n\nFor example, you might want to customize how images are displayed using the `@sveltejs/enhanced-img` plugin.\nFirst, define a custom node in `markdoc/nodes.ts`:\n\n```typescript\nimport type { Config } from \"markdoc-svelte\";\nimport { markdocPreprocess } from \"markdoc-svelte\";\n\nconst nodes: Config[\"nodes\"] = {\n  image: {\n    render: \"EnhancedImage\",\n    attributes: {\n      // Include the default image attributes\n      ...Markdoc.nodes.image.attributes,\n    },\n  },\n};\n```\n\nThen add an EnhancedImage component in `src/lib/components/EnhancedImage.svelte`:\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n  // Glob import all Markdown images\n  const imageModules = import.meta.glob(\n  '$lib/images/*.{avif,gif,heif,jpeg,jpg,png,tiff,webp,svg}',\n    {\n      eager: true,\n      query: {\n          enhanced: true,\n      },\n    }\n  ) as Record\u003cstring, { default: string }\u003e;\n\n  const { src, alt, ...restProps } = $props();\n\n  // Find the image module that matches the src\n  const matchingPath = Object.keys(imageModules).find((path) =\u003e path.endsWith(src));\n  const image = matchingPath ? imageModules[matchingPath].default : undefined;\n\u003c/script\u003e\n\n{#if image}\n  \u003c!-- Render the image with the enhanced-img plugin --\u003e\n  \u003cenhanced:img src={image} {alt} {...restProps} /\u003e\n{:else}\n  \u003cimg src={src} {alt} {...restProps} /\u003e\n{/if}\n```\n\nNow your EnhancedImage component handles images added through standard Markdown syntax:\n\n```markdown\n![A cat sleeping on a balcony](awesome-cat.png)\n```\n\n### Partials\n\n[Partials](https://markdoc.dev/docs/partials) are ways to reuse content across files (through [transclusion](https://en.wikipedia.org/wiki/Transclusion)).\nThe partials defined in your configuration must be a directory of Markdoc files.\n\nFor example, you could have a file structure like the following:\n\n```\n| markdoc/\n|-- partials/\n|   |-- content.mdoc\n|   └── post.mdoc\n```\n\nThese files can be included in other files as follows:\n\n```markdown\n---\ntitle: Hello World\n---\n\n# Hello World\n\nThis is a file that is processed by `markdoc-svelte`.\n\n{% partial file=\"content.mdoc\" %}\n\n{% partial file=\"post.mdoc\" %}\n```\n\n### Tags\n\n[Tags](https://markdoc.dev/docs/tags) are ways to extend Markdown syntax to do more.\nYou can add functionality through Svelte components\n\nFor example, you might want to create a custom Callout tag to highlight information on a page\n(these are also known as admonitions).\nFirst, define the tag in `markdoc/tags.ts`:\n\n```javascript\nimport type { Config } from \"markdoc-svelte\";\n\nconst tags: Config[\"tags\"] = {\n  callout: {\n    // The Svelte component to render the tag\n    render: \"Callout\",\n    // What tags it can have as children\n    children: [\"paragraph\", \"tag\", \"list\"],\n    // Define the type of callout through an attribute\n    attributes: {\n      type: {\n        type: String,\n        default: \"note\",\n        matches: [\"caution\", \"check\", \"note\", \"warning\"],\n        errorLevel: \"critical\",\n      },\n      title: {\n        type: String,\n      },\n    },\n  },\n};\n\nexport default tags;\n```\n\nThen create a Callout component for tag in `src/lib/components/Callout.svelte`:\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n  let { title, type, children } = $props();\n\u003c/script\u003e\n\n\u003cdiv class={`callout-${type}`}\u003e\n  \u003cdiv class=\"content\"\u003e\n    \u003cdiv class=\"copy\"\u003e\n      \u003cspan class=\"title\"\u003e{title}\u003c/span\u003e\n      \u003cspan\u003e{@render children()}\u003c/span\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n```\n\nThen you can use the Callout tag in a Markdoc file:\n\n```markdown\n---\ntitle: Hello World\n---\n\n{% callout type=\"caution\" title=\"Hello\" %}\nThis is a caution callout.\n{% /callout %}\n```\n\n### Typographer\n\nChoose whether to turn on typographic replacements from [markdown-it](https://github.com/markdown-it/markdown-it).\nSee the options in action at the [markdown-it demo](https://markdown-it.github.io/)\n(select or deselect `typographer`).\nDefaults to false.\n\n### Validation level\n\nThe preprocessor validates whether the Markdoc is valid.\nBy default, it throws an error on files for issues at the `error` or `critical` level.\nTo debug, you can set the level to a lower level to stop the build for any errors at that level or above.\nPossible values in ascending order: `debug`, `info`, `warning`, `error`, `critical`.\n\n### Variables\n\n[Variables](https://markdoc.dev/docs/variables) are ways to customize your documents at runtime.\nThis way the Markdoc content stays the same, but the generated HTML can vary,\nsuch as if you're publishing the same content to various sites.\n\nFor example, you might define a `$site.name` variable in `markdoc/variables.ts`:\n\n```javascript\nimport type { Config } from \"markdoc-svelte\";\n\nconst variables: Config[\"variables\"] = {\n  site: {\n    name: \"Markdoc Svelte\",\n  },\n}\n\nexport default variables\n```\n\nThen you can use the variable in a Markdoc file:\n\n```markdown\n---\ntitle: Hello World\n---\n\nThis is published on the {% $site.name %} site.\n```\n\n## Advanced\n\n### Markdoc limitations\n\nMarkdoc has a few Markdown syntax limitations, see [Markdoc FAQ](https://markdoc.dev/docs/faq).\n\n### @sveltejs/enhanced-img\n\nTo use the [enhanced-img plugin](https://svelte.dev/docs/kit/images#sveltejs-enhanced-img) with Markdown images, you can customize the default images Node with a custom Svelte component.\nSee the example [custom node](#nodes).\n\n### Page table of contents\n\nEach proccessed page automatically exports a `headings` property with all headings on the page and IDs for each.\nAdd IDs with [annotations](https://markdoc.dev/docs/syntax#annotations) or they are generated automatically.\nUse this list to generate a table of contents for the page, as in the following example:\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n  import type { PageProps } from './$types';\n\n  let { data }: PageProps = $props();\n  const { frontmatter, headings } = data.page;\n\n  // Filter only h1 and h2 headings\n  const filteredHeadings = headings?.filter((heading) =\u003e heading.level \u003c= 2) ?? [];\n\u003c/script\u003e\n\n\u003csvelte:head\u003e\n  \u003ctitle\u003e{data.page.frontmatter?.title ?? 'Undefined title'}\u003c/title\u003e\n\u003c/svelte:head\u003e\n\n{#if filteredHeadings.length \u003e 0}\n  \u003cul\u003e\n    {#each filteredHeadings as heading}\n      \u003cli\u003e\n        \u003ca href={`#${heading.id}`}\u003e{heading.text}\u003c/a\u003e\n      \u003c/li\u003e\n    {/each}\n  \u003c/ul\u003e\n{/if}\n\n\u003cdata.page.default /\u003e\n```\n\n### Index page example\n\nEach processed page exports a slug based on the file name.\nThis is a convenient way to generate an index page without reaching into the document.\n\nGlob import these slugs as data for your index page.\nFor example, if all of your pages end with the extension `.md`,\nAdd the following to `src/routes/blog/+page.ts`:\n\n```typescript\nimport type { MarkdocModule } from \"markdoc-svelte\";\n\nimport type { PageLoad } from \"./$types\";\n\nconst markdownModules = import.meta.glob(\"$lib/markdown/*.md\");\n\nexport const load: PageLoad = async () =\u003e {\n  const content = await Promise.all(\n    Object.values(markdownModules).map(async (importModule) =\u003e {\n      // Dynamically import each module\n      const module = (await importModule()) as MarkdocModule;\n      // Pass only slug and frontmatter to the page data\n      return {\n        slug: module.slug,\n        frontmatter: module.frontmatter,\n      };\n    }),\n  );\n  return { content };\n};\n```\n\nThen use this data to build an index page at `src/routes/blog/+page.svelte`:\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n  import type { PageProps } from './$types';\n\n  let { data }: PageProps = $props();\n  const { content } = data;\n\u003c/script\u003e\n\n\u003ch1\u003eTable of Contents\u003c/h1\u003e\n\u003cul\u003e\n  {#each content as item, i (item.slug)}\n    \u003cli class={'item-' + i}\u003e\n      \u003ca href=\"/{item.slug}\"\u003e\n        \u003ch2\u003e{item.frontmatter?.title || item.slug}\u003c/h2\u003e\n        {#if item.frontmatter?.description}\n          \u003cspan\u003e{item.frontmatter.description}\u003c/span\u003e\n        {/if}\n        {#if item.frontmatter?.published}\n          \u003cspan\u003e{item.frontmatter.published}\u003c/span\u003e\n        {/if}\n      \u003c/a\u003e\n    \u003c/li\u003e\n  {/each}\n\u003c/ul\u003e\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcolliercz%2Fmarkdoc-svelte","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcolliercz%2Fmarkdoc-svelte","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcolliercz%2Fmarkdoc-svelte/lists"}