Installation

You can use Markdown by adding one of the following Vite plugins.

Vue

Markdown plugins compatible with Vue:

Example:

React

Markdown plugins compatible with React:

Example:

Metadata

There are several techniques for defining metadata (e.g. the date and author of a page).

The preferred technique depends on whether you want to define global or local metadata.

Global metadata

What is global metadata? For example, you want to show a list of all your blog posts always on the left side of your website.

2024-01-01 New Year 2024 Resolution
2023-12-20 Wrapping up 2023
2023-06-15 My summer 2023

Because this list is shown on the left of every page, the publishing date and title of all blog posts is needed for rendering any page: the metadata needs to be accessible globally.

For global metadata, we recommend:

⚠️

You may be tempted to use import.meta.glob() to retrieve metadata defined inside all +Page.md files, but we discourage this approach: loading all +Page.md files at once contradicts Vite's lazy-transpiling approach and it would significantly slow down development speed.

Local metadata

What is local metadata? For example, you want to show detailed information below a blog post, such as the author's name. This metadata is shown only for that page and, therefore, needs to be accessible only locally.

For local metadata, we recommend:

With a metadata.js file

A simple way to define metadata is to define a metadata.js file that contains global metadata.

// /pages/metadata.js
 
// This metadata is available to every page
export const metadata = [
  {
    url: '/blog/introducing-vike',
    title: 'Introducing Vike',
    date: new Date('2024-01-01')
  },
  {
    url: '/blog/v1',
    title: 'v1.0.0 release',
    date: new Date('2025-07-01')
  }
]
// /pages/metadata.ts
 
// This metadata is available to every page
export const metadata = [
  {
    url: '/blog/introducing-vike',
    title: 'Introducing Vike',
    date: new Date('2024-01-01')
  },
  {
    url: '/blog/v1',
    title: 'v1.0.0 release',
    date: new Date('2025-07-01')
  }
]
// /pages/+Layout.jsx
 
import React from 'react'
import { metadata } from './metadata'
 
export function Layout({ children }) {
  // Current URL
  const { urlPathname } = usePageContext()
  // The page's metadata
  const { title } = metadata.find(({ url }) => url === urlPathname)
 
  return (
    <>
      {/* Show the list of blog posts */}
      <LeftSidebar>
        <p>Blog posts:</p>
        <ul>
          {metadata.map(({ title, url, date }) => (
            <li>
              <a href={url}>{data + title}</a>
            </li>
          ))}
        </ul>
      </LeftSidebar>
 
      {/* The page's content */}
      <Content>
        <h1>{title}</h1>
        {/* children is pageContext.Page which is the component defined by +Page.md */}
        {children}
      </Content>
    </>
  )
}
// /pages/+Layout.tsx
 
import React from 'react'
import { metadata } from './metadata'
 
export function Layout({ children }: { children: React.ReactNode }) {
  // Current URL
  const { urlPathname } = usePageContext()
  // The page's metadata
  const { title } = metadata.find(({ url }) => url === urlPathname)
 
  return (
    <>
      {/* Show the list of blog posts */}
      <LeftSidebar>
        <p>Blog posts:</p>
        <ul>
          {metadata.map(({ title, url, date }) => (
            <li>
              <a href={url}>{data + title}</a>
            </li>
          ))}
        </ul>
      </LeftSidebar>
 
      {/* The page's content */}
      <Content>
        <h1>{title}</h1>
        {/* children is pageContext.Page which is the component defined by +Page.md */}
        {children}
      </Content>
    </>
  )
}

See also:

// /pages/blog/introducing-vike/+Page.md
 
We're thrilled to officially introduce Vike.
// /pages/blog/v1/+Page.md
 
The `v1.0.0` release signals that Vike is ready for prime time: it now includes
all essentials you'd expect from a frontend framework with a robust design.

With a custom setting (eager)

You can use meta to create a custom setting for defining global metadata.

/pages/+config.js
/pages/+onCreateGlobalContext.server.js
/pages/+Layout.jsx
/pages/2024-new-year/+Page.md
/pages/2024-new-year/+metadata.js
/pages/+config.ts
/pages/+onCreateGlobalContext.server.ts
/pages/+Layout.tsx
/pages/2024-new-year/+Page.md
/pages/2024-new-year/+metadata.ts
// /pages/+config.js
 
export default {
  meta: {
    // Create +metadata setting
    metadata: {
      // Make +metadata available to all pages
      eager: true,
      env: {
        server: true,
        // Instead of `client: true`, we use onCreateGlobalContext() with passToClient to
        // be able to determine exactly what metadata is sent to the client-side.
        client: false
      }
    }
  },
  passToClient: [
    // Make globalContext.posts available on the client-side
    'posts'
  ]
}
// /pages/+config.ts
 
import type { Config } from 'vike/types'
 
export default {
  meta: {
    // Create +metadata setting
    metadata: {
      // Make +metadata available to all pages
      eager: true,
      env: {
        server: true,
        // Instead of `client: true`, we use onCreateGlobalContext() with passToClient to
        // be able to determine exactly what metadata is sent to the client-side.
        client: false
      }
    }
  },
  passToClient: [
    // Make globalContext.posts available on the client-side
    'posts'
  ]
} satisfies Config

When setting eager: true the setting is available globally to all pages.

// /pages/2024-new-year/+metadata.js
 
export const metadata = {
  title: 'New Year 2024 Resolution'
}
// /pages/2024-new-year/+metadata.ts
 
export const metadata = {
  title: 'New Year 2024 Resolution'
}

Unlike in the example below you cannot define +metadata inside +Page.md, because +Page.md is only loaded when rendering that page. Consequently, the content of +Page.md isn't available when rendering another page.

// /pages/+onCreateGlobalContext.server.js
// Environment: server
 
export async function onCreateGlobalContext(globalContext) {
  const pages = globalContext.pages
  const posts = Object.values(pages).map((page) => {
    const { metadata } = page.config
    const post = {
      url: page.route,
      title: metadata.title
    }
    return post
  })
  globalContext.posts = posts
}
// /pages/+onCreateGlobalContext.server.ts
// Environment: server
 
import type { GlobalContextServer } from 'vike/types'
 
export async function onCreateGlobalContext(globalContext: GlobalContextServer) {
  const pages = globalContext.pages
  const posts = Object.values(pages).map((page) => {
    const { metadata } = page.config
    const post = {
      url: page.route,
      title: metadata.title
    }
    return post
  })
  globalContext.posts = posts
}
 
declare global {
  namespace Vike {
    interface GlobalContext {
      posts: {
        url: string,
        title: string,
      }[]
    }
  }
}

See also:

// pages/+config.js
 
export default {
  passToClient: ['posts']
}
// pages/+config.ts
 
import type { Config } from 'vike/types'
 
export default {
   passToClient: ['posts']
} satisfies Config
// /pages/+Layout.jsx
// Environment: server & client
 
import React from 'react'
import { usePageContext } from 'vike-react/usePageContext' // or vike-{vue,solid}
 
export function Layout({ children }) {
  const pageContext = usePageContext()
  const { posts } = pageContext.globalContext
  return (
    <>
      {/* Show the list of blog posts */}
      <LeftSidebar>
        <p>Blog posts:</p>
        <ul>
          {posts.map((post) => (
            <li>
              <a href={post.url}>{post.title}</a>
            </li>
          ))}
        </ul>
      </LeftSidebar>
 
      {/* The page's content */}
      <Content>
        <h1>{title}</h1>
        {/* children is pageContext.Page which is the component defined by +Page.md */}
        {children}
      </Content>
    </>
  )
}
// /pages/+Layout.tsx
// Environment: server & client
 
import React from 'react'
import { usePageContext } from 'vike-react/usePageContext' // or vike-{vue,solid}
 
export function Layout({ children }: { children: React.ReactNode }) {
  const pageContext = usePageContext()
  const { posts } = pageContext.globalContext
  return (
    <>
      {/* Show the list of blog posts */}
      <LeftSidebar>
        <p>Blog posts:</p>
        <ul>
          {posts.map((post) => (
            <li>
              <a href={post.url}>{post.title}</a>
            </li>
          ))}
        </ul>
      </LeftSidebar>
 
      {/* The page's content */}
      <Content>
        <h1>{title}</h1>
        {/* children is pageContext.Page which is the component defined by +Page.md */}
        {children}
      </Content>
    </>
  )
}

See also:

With a custom setting

You can use meta to create a custom setting for defining local metadata.

// /pages/2024-new-year/+Page.mdx
 
export const metadata = {
  author: {
    firstName: 'John',
    lastName: 'Smith',
    country: 'England'
  }
}
 
## Some Markdown
 
This page uses [markdown](https://en.wikipedia.org/wiki/Markdown).

MDX allows you to export JavaScript values in .mdx files.

Usually, Vike forbids +Page.js files to have "side exports": the +Page.js should only export the value of the Page setting. But, for improved DX, Vike allows markdown files (such as +Page.mdx) to export the value of other settings.

// /pages/+config.js
 
// Create +metadata setting
export default {
  meta: {
    metadata: {
      env: { server: true, client: true }
    }
  }
}
// /pages/+config.ts
 
import type { Config } from 'vike/types'
 
// Create +metadata setting
export default {
  meta: {
    metadata: {
      env: { server: true, client: true }
    }
  }
} satisfies Config

With frontmatter

Some markdown processors have support for a so-called frontmatter:

---
title: A Markdown Page
author: John Smith
---
 
## Some Markdown
 
This page uses [markdown](https://en.wikipedia.org/wiki/Markdown).

You can use such frontmatter to define local metadata.

⚠️

We discourage using frontmatter for defining global metadata, because loading all +Page.md files at once contradicts Vite's lazy-transpiling approach and it would significantly slow down development speed.

The markdown processor exposes the data defined by the frontmatter as an export, for example export { frontmatter }. You can access it by using meta to define a +frontmatter setting (or whatever the name of the export is).

// pages/+config.js
 
export default {
  meta: {
    // Create new setting +frontmatter
    frontmatter: {
      env: { server: true, client: true }
    }
  }
}
// pages/+config.ts
 
export default {
  meta: {
    // Create new setting +frontmatter
    frontmatter: {
      env: { server: true, client: true }
    }
  }
} satisfies Config

You can then use pageContext.config to access it:

// /pages/+Layout.jsx
 
import React from 'react'
import { usePageContext } from 'vike-react/usePageContext' // or vike-{vue,solid}
 
export function Layout({ children }) {
  const pageContext = usePageContext()
  const { frontmatter } = pageContext.config
  return (
    <>
      <h1>{frontmatter.title}</h1>
      {children}
      <footer>
        <p>Written by {frontmatter.author}.</p>
      </footer>
    </>
  )
}
// /pages/+Layout.tsx
 
import React from 'react'
import { usePageContext } from 'vike-react/usePageContext' // or vike-{vue,solid}
 
export function Layout({ children }: { children: React.ReactNode }) {
  const pageContext = usePageContext()
  const { frontmatter } = pageContext.config
  return (
    <>
      <h1>{frontmatter.title}</h1>
      {children}
      <footer>
        <p>Written by {frontmatter.author}.</p>
      </footer>
    </>
  )
}

See also:

See also