Nuxtdown
Support for markdown content in your nuxt.js site.
Note: This is a fork of Nuxtent with some additional features.
Summary
Nuxtdown allows you to use markdown files as content in your Nuxt.js application. It combines the ease-of-use of markdown for writing content, with the power of Nuxt.js for building advanced sites and web applications.
In combination with Nuxt's generate mode to generate a static site, Nuxtdown allows you to seamlessly port your site's content from Jekyll, Hugo, or other static site generators.
Features
- Simple configuration
- Allows you to override settings for the markdown parser (markdown-it), and use its plugins
- Support both blog posts or similar flat content structures, and a nested hierarchy of markdown content
- Adds useful info to your markdown page's meta info, including:
- Any data your specify in the config file
- A breadcrumbs trail for hierarchical markdown content
- (the info for) a table of contents
- Support for using vue components inside your markdown content
- Adds the
$content
helper to Nuxt to allow your to access your markdown content and metadata inside Nuxt pages
Quick Start
If you already have a site, follow these three steps:
- Step 1: Pick your mode
- Single directory of markdown files --- typically blog posts or similar
- Nested structure of markdown files --- typically documentation or similar
- Step 2: Place your files in the
content
directory` - Step 3: Configure your content
That's it, you're done.
Installation
npm install nuxtdown --save
Then, under nuxt.config.js
install the module:
modules: [
'nuxtdown'
]
Configuration
Nuxtdown looks for its configuration in the nuxtdown.config.js
file in the root of your application.
Content configuration
Here are the configuration options for your content:
Name | Description | Default |
---|---|---|
page |
The Nuxt page that will serve the dynamic routes to your content. | |
permalink |
The permalink under which your content will be available. | |
isPost |
Whether your markdown files have YYYY-MM-DD-slug.mg format or not. | true |
data |
An object of key/value pairs that will get added to your frontmatter. | false |
breadcrumbs |
Whether to generate a breadcrumb trail or not. | false |
toc |
Controls the (optional) generate of a table of contents. (see below) | false |
markdown |
Options for the markdown parser. (see below) |
Each of these apply to a folder in the content
directory. You can have multiple, and different, configurations side by side.
page
This is the Nuxt page that will serve the dynamic routes to your content. As such, it's always a file somewhere in app/pages
that starts with an underscore (as that's how Nuxt knows it's for dynamic routes)
This config option has no default and must be configured.
permalink
The permalink under which the content will be available in the browser.
Note: This will be different from the link under which we access it in the content api (see below).
The permalink configuration can take a number of placeholdes:
:slug
: A slugified version of your filename (without the date for posts):year
: Year of the post (only relevant whenisPost
istrue
):month
: Month of the post (only relevant whenisPost
istrue
):day
: Day of the post (only relevant whenisPost
istrue
):section
: The subdirectory within your content directory. See below.
The :section
part of your permalink deserves a bit more attention.
It is the subdirectory path from the top of your content group to your markdown file.
For a markdown file in the root of our content folder, the section wil be /
. But for subdir1/subdir2/somepage.md
the section will be subdir1/subdir2
.
This config option has no default and must be configured.
isPost
Whether or not your markdown content are posts.
When this is true
(the default) your markdown filenames should start with YYYY-MM-DD-
which will be used as the post date.
When this is false
your markdown files should just have names like somepage.md
.
data
An object of key/value pairs that will get added to the frontmatter/metadata of all your markdown files for this content group.
breadcrumbs
If set to true
this will include metadata to build a trail from your content trunk to the content leaf.
It will not include trunk or leaf, but only the intermediate steps. For example:
- If our content is in folder
docs
:docs/about.md
will have no breadcrumbs, asdocs
is the trunk anddocs/about
is the leafdocs/example/something.md
will have 1 breadcrumb fordocs/example
Here is a real-world sample of the data generated by the breadcrumbs
option for a docs/patterns/simon
page:
breadcrumbs:
0:
frontMatter
layout: "cards"
title: "Patterns"
permalink: "/patterns/"
nocomments: true
contentLocale: "en"
permalink: "/docs/patterns"
toc
Whether to generate (the metadata for) a table of contents or not. Defaults to false
, no table of contents.
This uses the markdown-it-anchor plugin for markdown-it under the hood.
toc
as an integer
If you set toc
to an integer, the anchors plugin will be configured as follows:
Name | Description |
---|---|
level |
The value your set toc to in your content configuration |
permalink |
true |
permalinkClass |
nuxtdown-toc |
permalinkSymbol |
🔗 |
toc
as an object
If you don't like these defaults, you can pass your own configuration object to configure the plugin. See the official documentation
Excluding titles from your table of contents
To prevent a title from being added to the toc, give it the .notoc
CSS class.
Tip: You can use the markdown-it-attrs plugin to do this in your markdown.
Here is a real-world sample of the data generated by the toc
option for a blog post:
toc
topLevel: 2
items:
looking-back-at-september-and-a-bit-of-august
level: 2
title: "Looking back at september, and a bit of august"
link: "#looking-back-at-september-and-a-bit-of-august"
my-name-is-simon-and-im-complicated
level: 3
title: "My name is Simon, and I’m complicated"
link: "#my-name-is-simon-and-im-complicated"
whats-your-problem-simon
level: 4
title: "What’s your problem Simon?"
link: "#whats-your-problem-simon"
is-it-time-for-an-overhaul
level: 4
title: "Is it time for an overhaul?"
link: "#is-it-time-for-an-overhaul"
dealing-with-email-delivery-issues
level: 2
title: "Dealing with email delivery issues"
link: "#dealing-with-email-delivery-issues"
The output will contain a topLevel
attribute that tells you what the highest level of heading is that is included
in your toc. This allows you to build a hierarchical table of contents where the top level is the actual
highest level of heading in your content.
markdown
This allows you to configure the markdown-it markdown-it markdown parser.
It takes a configuration object that allows you to configure the parser in three ways:
extend
the default options passed to the parser- Add
plugins
to the parser. customize
the parser after it was created.
Here's an example that does all three. It uses extend
to change the highlight function. Uses plugins
to load the
markdown-it-video plugin,
and uses customize
to add onion
as a TLD to linkify.
markdown: {
extend(config) {
config.highlight = (code, lang) => {
return `<pre class="language-${lang}"><code class="language-${lang}">${Prism.highlight(code, Prism.languages[lang] || Prism.languages.markup)}</code></pre>`
}
},
plugins: {
video: require('markdown-it-video')
},
customize(parser) {
parser.linkify.tlds('onion')
}
}
Example
Here's an example that covers most use cases:
Note that the path doesn't have to be a top-level directory. It can also be something like
docs/translations/fr
// nuxtdown.config.jsmoduleexports = content: // My blog posts are in content/blog 'blog' page: '/blog/_blogpost' permalink: '/blog/:slug' // My documentation is in content/docs 'docs' page: '/docs/_page' permalink: '/docs/:section*/:slug' isPost: false data: generatedBy: 'Nuxt with the nuxtdown module' breadcrumbs: true toc: 1 markdown: plugins: toc: permalinkClass: 'nuxtdown-toc' permalinkSymbol: '↗' attrs: figures: figcaption: true video: api: baseURL: 'http://localhost:3000' browserBaseURL: processenvFREESEWING_BROWSER_BASE_URL
We have two content groups:
- The
blog
directory holds posts (remember,isPost
defaults totrue
)- Their dynamic routes will be served by the page
approot/pages/blog/_blogpost
- Their permalink will be
/blog/:slug
- Their dynamic routes will be served by the page
- The
docs
directory holds regular markdown files- Their dynamic routes will be served by the page
approot/pages/docs/_blogpost
- Their permalink will be
/docs/:section*/:slug
- These are not posts
- We want to inject
generatedBy
into the frontmatter - We want a breadcumbs trail to be generated
- We want the elements for a table of contents to be generated
- We want to override some of the settings of the markdown parser:
- Change the class for anchors
- Change the symbol used for anchors
- Load three extra plugins for the markdown parser
- Their dynamic routes will be served by the page
In this example, our content
direcory is structured like this:
- Nuxt app root
- content
- blog
- 2017-11-14-hello-world.md
- 2017-12-14-happy-holidays.md
- 2018-01-14-pizza-party.md
- 2018-03-21-when-in-rome.md
- docs
- about.md
- examples
- building-a-pizza-oven.md
- index.md
- pizza-from-scratch.md
- index.md
- license.md
- tutorial
- index.md
- part-1.md
- part-2.md
- part-3.md
- blog
- content
Which would lead to this permalink structure on our website:
- /blog/hello-world/index.html
- /blog/hello-world/happy-holidays/index.html
- /blog/hello-world/pizza-party/index.html
- /blog/hello-world/when-in-rome/index.html
- /docs/index.html
- /docs/about/index.html
- /docs/license/index.html
- /docs/examples/index.html
- /docs/examples/building-a-pizza-over/index.html
- /docs/examples/pizza-from-scratch/index.html
- /docs/tutorial/index.html
- /docs/tutorial/part-1/index.html
- /docs/tutorial/part-2/index.html
- /docs/tutorial/part-3/index.html
Requests for a blog posts are served by the blog/_postpost.vue
page.
Inside it, we have this:
{ return post: await app }
Note that what we are fetching from the content-api is content-group
+permalink
, like this:
/content-api/ section permalink
For example, a blog post in section blog
with permalink /blog/hello-world
will be fetched in async from /content-api/blog/blog/hello-world
API configuration
Apart from the content, you can (and should) configure how to access the content api
in nuxtdown.config.js
. Here's an example:
api: baseURL: 'http://localhost:3000' browserBaseURL: processenvFREESEWING_BROWSER_BASE_URL
baseURL
The url to the content-api used by node, and by the browser in development.
This is probably going to be http://localhost:3000
browserBaseURL
The url to the content-api used by the browser in production.
Something like https://my-website.com/content-api
In the example above, we use an environment variable for this to differentiate between development and production environments.
License
Copyright (c) 2017 Nuxt Community