Building the Surton Blog

Building the Surton Blog

Coaxing Next.js, Tailwind, and MDX to play well together

Ben HaydenBen HaydenFri Apr 14 2023

In this post, we're going to do a technical deep dive about how our website and blog are built. is built using Next.js, TailwindCSS, and MDX. Next.js powers the page logic and rendering, Tailwind for styling, and MDX for writing blog posts. These are all great technologies, but it can be overwhelming trying to figure out how to put them all together. We're going to start out by assuming we have a Next.js site (feel free to start from some template you have found!), and go through installing and configuring MDX.

npm install @next/mdx @mdx-js/loader @tailwindcss/typography rehype-highlight highlight.js

Now, rehype-highlight is an ES module, so we'll need to import it using ES module syntax. So, if you have a next.config.js, you'll need to move it to a next.config.mjs file extension. Update the contents to look like the below:

import nextMDX from '@next/mdx'
import rehypeHighlight from 'rehype-highlight'

const withMDX = nextMDX({
  extension: /\.mdx?$/,
  options: {
    rehypePlugins: [rehypeHighlight],

/** @type {import('next').NextConfig} */
const nextConfig = {
  pageExtensions: ['ts', 'tsx', 'js', 'jsx', 'md', 'mdx'],
  reactStrictMode: true,
  images: {
    unoptimized: true,
  output: 'export',

export default withMDX(nextConfig)

We installed the @tailwindcss/typography plugin to style our Markdown-generated HMTL into something nicely styled without much effort on our part. An updated Tailwind config will look something like

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [... ], // your content files (js and ts)
  plugins: [..., require('@tailwindcss/typography')], // include your other plugins

While on the topic of styles, make sure and include the Highlight.js theme for your syntax highlighting blocks in MDX. Any styles listed in the highlight styles directory are valid options.

// inside of your _app.js
import 'highlight.js/styles/monokai.css' // Your theme here

You then need to create a pages/posts directory and nest the {year} and {month} as subdirectories - something like this: pages/posts/2023/04. Add a my-first-post.mdx file. This will be the first blog post. The contents of the file should look like the below:

// You can share a Post layout as a React Component to be used in all your posts
import BlogPostLayout from '@/components/BlogPostLayout'

// You can also share metadata about your post as a variable
export const meta = {
  title: 'My First Post',
  author: 'Ben Hayden',
  date: '2023-04-14',
  tags: ['nextjs', 'development', 'markdown', 'learning'],

// The content of the post goes here. Any markdown syntax **will be** rendered as plain HTML.

export default ({ children }) => (
  <BlogPostLayout meta={meta}>{children}</BlogPostLayout>

After running next dev, you can then visit the URL in the browser and view your MDX file rendered as beautiful HTML. If you'd like to build an index page or collection of your blog posts, but keep the site as a static HTML export, you can use getStaticProps() to do so.

export async function getStaticProps() {
  // getPosts is a server-side function that loops through the posts directory
  // and extracts the metadata of all the posts and puts them into a single sortable list.
  const posts = await getPosts({
    sortBy: 'date',
    sortDirection: 'desc',
  return {
    props: {

Now it's time to - (theoretically) - deploy the blog. We have chosen to deploy our site as a static HTML export and host it on AWS CloudFront via AWS CDK. To do so requires two steps:

next build
cdk deploy

The CDK constructs will make sure we upload the latest contents to an S3 bucket and such our changes will be reflected on the live site.

Consider this post a “captain's log” — I'll be adding more to this post as we continue to build out the blog. If you have any questions, feel free to reach out to us on Facebook or LinkedIn.