MDX

An alternative way to author content is using .mdx files (Markdown JSX). These files are authored as Markdown, but they are compiled down to Qwik components. In addition to Markdown syntax, .mdx files can also refer to other components.

Let's assume you have your routes set up like this:

src/
โ””โ”€โ”€ routes/
    โ””โ”€โ”€ some/
        โ””โ”€โ”€ path/
            โ””โ”€โ”€ index.mdx    # https://example.com/some/path
src/routes/some/path/index.mdx
---
title: Hello World Title
---
 
This is a simple hello world component.
 

The above component will be rendered at https://example.com/some/path.

Importing other components.

MDX is a creative opportunity for you to come up with new content quickly ("Qwikly" ๐Ÿ™‚) and if you need more interaction on your page you can seamlessly integrate your Qwik components like so:

src/
โ”œโ”€โ”€ components/
|   โ””โ”€โ”€ counter
โ”‚       โ””โ”€โ”€  counter.tsx
โ””โ”€โ”€ routes/
    โ””โ”€โ”€ some/
        โ””โ”€โ”€ path/
            โ””โ”€โ”€ index.mdx    # https://example.com/some/path
src/routes/some/path/index.mdx
---
title: Hello World Title
---
import { Counter } from "../../../components/counter/counter";
 
This is a simple hello world component.
 
<Counter />
 
src/components/counter/counter.tsx
import { component$, useSignal } from '@qwik.dev/core';
 
export const Counter = component$(() => {
  const count = useSignal(0);
 
  return (
    <button class="counter" type="button" onClick$={() => count.value++}>
      Increment {count.value}
    </button>
  );
});

Note: A key difference between Qwik City and many current meta-frameworks is directory-based routing. Every route needs to be defined as a-directory/index.(tsx,ts,js,jsx,md,mdx).

In other meta-frameworks you're used to about.mdx will render a route http://example.com/about. However, this will not work in Qwik City. You must rename the file to about/index.mdx for Qwik City to know to render it.

Disabling default MDX plugins included.

Qwik City includes 3 plugins by default.

These plugins can be disabled independently in the following way:

vite.config.js
import { defineConfig } from 'vite';
import { qwikCity } from '@qwik.dev/city/vite';
// See below
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
export default defineConfig(() => {
  return {
    plugins: [
      qwikCity({
        mdxPlugins: {
          remarkGfm: false,
          rehypeSyntaxHighlight: false,
          rehypeAutolinkHeadings: false,
        },
        mdx: {
          rehypePlugins: [
            // Plugins can now be added manually to use a different configuration
            [rehypeAutolinkHeadings, { behavior: 'wrap' }],
          ],
        },
      }),
      /* the rest of the configuration */
    ],
  };
});

Open Graph Properties

You can use og or opengraph property to define Open Graph protocol metadata.

title: My Title
description: My Description
og:
  - title: My Custom Title
    description: true
  - image: https://example.com/rock.jpg
    image:alt: A shiny red apple with a bite taken out
  - image: https://example.com/rock2.jpg

Setting og:title or og:description to true will check and use outside title and description property instead. Thus, you can avoid writing a same title and description twice.

The above example will generate the following HTML code.

<title>My Title</title>
<meta name="description" content="My Description" />
 
<meta property="og:title" content="My Custom Title" />
<meta property="og:description" content="My Description" />
 
<meta property="og:image" content="https://example.com/rock.jpg" />
<meta property="og:image:alt" content="A shiny red apple with a bite taken out" />
 
<meta property="og:image" content="https://example.com/rock2.jpg" />

Reading frontmatter data

Frontmatter keys are accessible by leveraging the useDocumentHead() hook.

src/routes/some/path/index.mdx
---
title: Hello World Title
tags:
  - super
  - exiting
  - docs
---
import { Tags } from "../../../components/tags/tags";
 
This is a simple hello world component.
 
<Tags />
src/components/tags/tags.tsx
import { component$ } from '@qwik.dev/core';
import { useDocumentHead } from '@qwik.dev/city';
 
export const Tags = component$(() => {
  const { frontmatter } = useDocumentHead();
  
  if (frontmatter.tags.length === 0) {
    return null;
  }
  
  return (
    <ul>
      {frontmatter.tags.map((tag: string) => (
        <li key={`tag-${tag}`}>{tag}</li>
      ))}
    </ul>
  );
});

Contributors

Thanks to all the contributors who have helped make this documentation better!

  • manucorporat
  • adamdbradley
  • Oyemade
  • mhevery
  • nnelgxorz
  • cunzaizhuyi
  • the-r3aper7
  • zanettin
  • ihaback
  • YannickFricke
  • mrhoodz
  • erikras
  • hamatoyogi