I'm building a blogging platform (Writizzy), an alternative to Ghost or Substack.
Under the hood I use Nuxt, but I don't use nuxt-content to have more flexibility, so I use the Nuxt-Mdc module directly. This module allows you to extend markdown to include custom components, such as image galleries, embedded YouTube players, and more.
However, Writizzy offers themes, and we want to easily customize components for each theme.
In this article, I'll show you how to use Nuxt-Mdc for this fairly common use case.
Nuxt-mdc
The nuxt-mdc module is used by nuxt content, but it can also be used directly to parse markdown content and render it as HTML. It's a core building block of Nuxt-Content, but fortunately it works as a standalone module.
Among Nuxt-mdc's features, you can use MDC components.
An MDC component is essentially a markdown syntax that calls a Vue component for rendering.
For example, with this markup:
::card
The content of the card
::
We're telling Nuxt-mdc to use the Card component to render the block above.
This is also what Nuxt-mdc uses to render all Prose components, which are custom components for headings, links, blockquotes, etc., created after markdown parsing. Internally, each standard Markdown element is transformed into MDC components. For example:
## a title is transformed into
::h2
a title
::
(This is a simplification. In reality, it's more of a substitution at the renderer level. The renderer reads the AST tree containing an h2 node and decides to use the h2 component to render it. But this is a good way to think about it.)
This feature allows Nuxt-Mdc users to customize the rendering of each MDC component, including Prose components.
To override a Prose component or create a custom component, you just need to place them in the app/components/mdc directory of your Nuxt application.
But what if you want these components to change based on the selected theme? That's exactly the question I faced with Writizzy.
A theming system for components
In Writizzy, I'm developing a theming system. You can choose your blog's appearance from the themes listed here. I partially reused what I had already built for Bloggrify (an open source project for creating static blogs).
But with Writizzy, I was quite unsatisfied with this solution, especially for custom components. Obviously, the appearance of custom components should change based on the theme.
It turns out there's an undocumented feature in Nuxt-Mdc that does exactly this.
By default, the documentation recommends using the MDC component to display markdown, for example:
<script setup lang="ts">
const md = `
::alert
Hello MDC
::
`
</script>
<template>
<MDC :value="md" tag="article" />
</template>
However, MDC uses **MDCRenderer **behind the scenes. And looking at MDCRenderer's source code, we notice an interesting prop: components
This prop allows you to pass the list of components to use.
By default, MDCRenderer will look in this list first, then fall back to components/mdc, so you can override only the components you want to modify:
<script setup lang='ts'>
const mdcComponents = {
callout: TerminalCallout,
blockquote: TerminalBlockquote,
pre: TerminalCode
}
</script>
<template>
<MDCRenderer
:body="ast.body"
:data="ast.data"
:components="mdcComponents"
/>
</template>
And that's it!
Now you can customize each component based on the theme.
This approach works well for Writizzy, and I plan to apply it to Bloggrify as well. If you're using Nuxt-MDC with a theming system, I think this is the cleanest solution—even though it deserves to be officially documented.
I've opened an issue to clarify its status. In the meantime, it's working in production on Writizzy without any issues.
No comments yet. Be the first to comment!