Composing Documents with MDX: Markdown for the Component Era

Eden Ella
Bits and Pieces
Published in
6 min readJun 24, 2020

--

https://bit.dev/eden/markdown-components

In this tutorial, we’ll create React UI components and MDX markdown components, to compose and style a markdown document.

We’ll then publish all components to Bit, to make them available for reuse in future projects.

Building with reusable React components makes a lot of sense. It speeds up delivery, keeps a consistent UI across related projects, and helps in building apps that scale.

The same is true for building with reusable content components. It speeds up delivery (as modular content is easier to reuse), it helps in keeping a consistent “voice & tone” that is true to our brand’s identity, and it helps in organizing and maintaining content across projects.

Modularity in content is possible thanks to MDX 🙏

MDX syntax can be boiled down to being JSX in Markdown. It’s a superset of Markdown syntax that also supports importing, exporting, and JSX.

mdxjs.com

So, without further ado, these are the steps:

  1. Create a new Gatsby website and set it up to use MDX files
  2. Write a few (.md\.mdx) content components
  3. Create a few UI components (for styling). Then, compose and style a new document.
  4. Publish all content and UI components with Bit, for future compositions.

1. Set up a Gatsby site. Configure it to use MDX.

My project’s source-code on Github| My project’s components on Bit

We’ll first create a new Gatsby site with the ‘gatsby-starter-blog’ starter.

$ gatsby new mdx-gatsby-demo https://github.com/gatsbyjs/gatsby-starter-blog

Install all the necessary MDX packages.

$ npm install gatsby-plugin-mdx @mdx-js/mdx @mdx-js/react

Set up the gatsby-config.js to use the MDX plugin. For this project, we’ll set the MDX plugin to handle both .md and .mdx extensions.

plugins: [
{
resolve: `gatsby-plugin-mdx`,
options: {
extensions: [`.mdx`, `.md`]
},
},
// more plugins...

2. Write markdown components

Let’s create two markdown components in src/components/content directory.

The first will be ParagraphDefinition.md :

# ParagraphThat is a paraggraph. A paragraph (from the [Ancient Greek](https://en.wikipedia.org/wiki/Ancient_Greek) παράγραφος, parágraphos, "to write beside") is a self-contained unit of a discourse in writing dealing with a particular point or idea. A paragraph consists of one or more sentences. Though not required by the syntax of any language,paragraphs are usually an expected part of formal writing, used to organize longer prose.

The second will be ParagraphDefinitionHTML.md :

### The Paragraph elementThe HTML `<p>` element represents a paragraph. Paragraphs are usually represented in visual media as blocks of text separated from adjacent blocks by blank lines and/or first-line indentation, but HTML paragraphs can be any structural grouping of related content, such as images or form fields.Paragraphs are block-level elements, and notably will automatically close if another block-level element is parsed before the closing `</p>` tag.```
<p> An example for the paragraph element </p>
```

Notice how the above markdown components have the paragraph, header1, header3, link, and code markdown elements. We will customize their styling, in the next step.

3. Create UI components to customize the markdown’s styling. Then, compose the document.

We’ll use styled-components to create new UI components that will later be configured to style the markdown elements.

|-- src
|-- components
|-- content
|-- ParagraphDefinition.md
|-- ParagraphDefinitionHTML.md
|-- markdown-styles
|-- CodeBlock.js
|-- Header1.js
|-- Header3.js
|-- Link_.js
|-- Paragraph.js

For example, the CodeBlock component looks like so:

import styled from 'styled-components'const CodeBlock = styled.pre`
@import url(a-google-font-url);
font-family: 'Source Code Pro', monospace;
background-color: #000;
color: #fafafa;
padding: 20px;
border-radius: 5px;
display: block;
overflow-x: auto;
margin-right: 25px;
margin-left: 25px;
`
export default CodeBlock;

In our src/pages/index.js file, we’ll import all UI components.

import Header1 from '../components/markdown-styles/Header1'
import Header3 from '../components/markdown-styles/Header3'
import Paragraph from '../components/markdown-styles/Paragraph'
import Link_ from '../components/markdown-styles/Link_'
import CodeBlock from '../components/markdown-styles/CodeBlock'

We’ll then create an object with key-value pairs that determine which markdown element should be styled by which UI component.

const components = {
h1: Header1,
h3: Header3,
p: Paragraph,
a: Link_,
pre: CodeBlock
}

We’ll then use the MDXProvider context (from the MDX library) to pass our newly created object down through the document’s component tree.

We can now import our two markdown components and place them as children of the MDXProvider .

This will complete our document composition and styling 🎉

This composition ends up looking like so:

In this project, we’ve used the MDXProvider directly on our page but in a real project, it should probably be part of the layout.

4. Publish all components with Bit

Bit is a CLI tool and a cloud component hub that makes it quick and easy to publish, document, and organize components.

Example: React components published on Bit

We’ll first install Bit’s CLI tool:

$ npm install bit-bin --global

We’ll then initialize a Bit workspace in our project’s root directory:

$ cd mdx-gatsby-demo
$ bit init

We’ll tell Bit to track our soon-to-be-published components. When Bit tracks a component it looks for its dependencies to make sure it source-controls it and publishes it, with everything that it needs to work in other projects. In other words, it isolates it from its origin project.

We’ll start with the content components. We’ll make sure to track them under the ‘content’ namespace, to make it easier to find them, later on, on Bit’s component hub.

$ bit add src/components/content/* --namespace content

We’ll do the same with our styling/UI components. This time they’ll be placed under the ‘markdown-styles’ namespace. Notice how we use the * sign to select all components under each directory.

$ bit add src/components/markdown-styles/* --namespace markdown-styles

To compile our React components we’ll import and set a React compiler from Bit’s ENVs collection.

$ bit import bit.envs/compilers/react --compiler

It’s time to wrap things up. We’ll tag all our tracked components. This will build, run tests (if there were any), and set a version for them.

Even though we tag them “collectively”, they will still be packaged and published independently (we are not publishing a library).

$ bit tag --all

To publish the components to Bit’s registry we’ll first need to set up a free account and create a ‘component collection’.

Once done, we can log in and publish (“export”) our components to our newly created collection on Bit.

$ bit login
$ bit export <user-name>.<collection-name>

All styling and content components are now in your collection on Bit 🎉

https://bit.dev/eden/markdown-components

And, as promised, the components can be filtered by their namespaces. For example, let’s filter out the content components:

https://bit.dev/eden/markdown-components?namespaces=markdown-styles

That’s about it 👍

--

--