How to set up a Git Hub Pages Blog Site Using React and MDX

Tech: React - JavaScript - GitHub
Posted: 2021-02-15
Last Updated: 2021-02-15

The Why

I bumped into docs on github talking about hosting static sites on git hub pages or even creating a blog using Jekyll. I explored Jekyll and quickly realized I had no interest in learning Ruby just to create a blog site and wondered if I could do it with React (a technology I'm much more familiar with).

The How

Turns out you can with a couple of caveats:
  • You will need to use a module called git hub pages
  • You need to add in a little hack to make react router work. After searching the internet I found that the fine folks who maintain the create-react-app also tell you how to do it.
The one not so obvious part is the hack required for your index.html file and the 404.html page. This page explains it well with the gist of it being you add the 404 page given to your public folder and add a redirect script to your index.html file. When git hub refuses to route it calls 404 with everything you need to route and your redirect script routes it. It's then in the history and all is well next time you navigate to a menu item. Seems to work fine so going with it.
The next thing I wanted to do was use a combination of Markdown and React to I could have the freedom to write content in something as friendly as Markdown and still be able to use React for making stuff a little prettier and more functional. No shortage on options but what seemed friendliest was MDX. MDX gives you a scaffolding for your favorite framework here. Essentially MDX compiles your MDX into JSX (magical) and allows you to substitute your own React components for certain markdown tags. Relatedly, I like being able to use Rebass which allows you to drop pre-styled React components directly into our Markdown file (awesome!). Lastly a code highlighting component brings it home. I chose this one and adopted their example.
To see how I'm using these take a look at Post.js. Lines 8-13 define an object I pass to MDX (line 29) to tell it what React components to use for the corresponding Markdown tags. This is pretty much Ahh-mazing!
I can write Markdown and then automagically inject React Components in place of standard Markdown 😲!!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37import React, {Suspense} from 'react' import { Link } from "react-router-dom"; import { Box, Heading, Text } from 'rebass'; import blogContent from '../blogs/BlogMDX' import CustomCodeBlock from "../components/CodeBlock"; import {useTheme} from "@emotion/react"; const components = { pre: props => <div {...props} />, code: props => <CustomCodeBlock {...props} />, p: props => <Text ml={4} mt={2} mb={2} {...props} />, h1: props => <Box pl={3} pt={2} pb={2} {...props}><Heading {...props} as={'h1'}/></Box>, h4: props => <Box pl={3} pt={2} pb={2} {...props}><Heading {...props} as={'h4'}/></Box>, h5: props => <Box pl={3} pt={2} pb={2} {...props}><Heading {...props} as={'h5'}/></Box> } export const Post = (props) => { const post = props.history.location.state.post; const Content = blogContent(post.id) const theme = useTheme() return ( <div> <Link to="/blog/">{'<'} Back</Link> <Box p={4} > <Box pl={3} pt={2} pb={2} bg={theme.colors.primary}> <Heading as={'h1'} >{post.title}</Heading> </Box> <Box pt={2} pl={4}> <Suspense fallback={<div>Loading...</div>}> <Content components={components} post={post}/> </Suspense> </Box> </Box> </div> ) }

Gotchas

Like all technology -- just when you think it's the greatest thing you have ever seen it lets you down a little.
The first annoyance I ran into was hot reloading. Create-React-App's default configuration doesn't watch for mdx files and making it act right is a known issue. I dropped some nodemon on it
1nodemon --watch src start
and with a little config in package.json
1 2 3 4 5"nodemonConfig": { "ignore": ["test/*", "docs/*"], "delay": "2500", "ext": "mdx" },
most of my annoyance is gone but the existing hot reload and nodemon may get in the occasional slap fight and browser cache will seek to do what it does to developers.
Second was how to dynamically load blog content. The importMDX function that is used to import my blog MDX files doesn't let me pass it a variable. The only solution I could figure out was to create a BlogMDX.js file where I import each blog file and use a switch statement to return the one I want.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18import {lazy} from "react"; import {importMDX} from "mdx.macro"; const blog1 = lazy(() => importMDX('../blogs/1/index.mdx')) const blog2 = lazy(() => importMDX('../blogs/2/index.mdx')) const blogContent = (id) => { switch(id) { case 1: return blog1; case 2: return blog2; default: return 'problem' } }; export default blogContent;
I do ths same thing with images but will leave it to you to see what's happening there. (Hint look at images.js in blogs and Post.js)


When your app is compiled by
1npm run predeploy
it's all statically compiled so it works for now ... until further enlightenment is bestowed upon me.

Bringing it all home

Ultimately I end up with the following:

  • A data.json file with essentially metadata about each blog post that I use to render my list of posts.
  • A BlogMDX.js file that imports each mdx and returns the proper one based on blog id.
  • A theme file I use to drive mobile responsiveness and common color themes.
  • Two branches
    • One where I push all my code - daCode branch
    • The main branch , that I let github pages push the built code to, where it shows up as my github organization page.

Comments