Hey, folks 👋. Today we are going to build a new blog from scratch using Remix, MDX and TailwindCSS
🤔 What’s Remix? Yet another JavaScript framework
Remix is a full-stack web framework based on web fundamentals and modern UX. It is created by the team of React Router. Remix isn’t any brand new framework it had been over for a year but it was a paid framework over then but now the time had been changed and Remix is now free and open-source software 🚀.
Remix is a React-based framework that allows to you render code on the server-side. Wait for a second 🤔 Doesn’t NextJS do the same thing?
Remix took the old problems but approached them in a new style 🐱💻.
Remix only does Server Side Rendering (SSG), no Static Site Generation (SSG), and Incremental Static Regeneration (ISR) like NextJS.
Applications which use Static Site Generation (SSG) are fast, easy to deploy but it is really hard to use dynamic data, as the pages would be re-built every time the dynamic data has been changed. In Remix, we are only doing Server Side Rendering (SSG), which is great for dynamic data but it would be hard to deploy as you would need to have an actual server to run it.
Remix is suitable for applications that have multiple pages and which depend on some sort of dynamic data
🛠 Setting up the project
Let’s set up our project before getting started to code.
-
Create a new folder for our remix blog
mkdir remix-blog
-
Navigate into that folder
cd remix-blog
-
Open that folder in VSCode
code .
-
Initialize remix project in that folder
npx create-remix@latest
- The path of the remix application would be
./
, as we have already created a folder of our project - We would be going to deploy our remix application on Vercel
- We are going to be using JavaScript for this project
- The path of the remix application would be
-
Starting a local development server
npm run dev
This would start a local development server at localhost:3000
📁 Understanding the folder structure
The folder structure of a remix application is pretty simple.
-
api
folder contains all the backend/api code. -
app
folder contains most of the frontend code.-
app/routes
folder contains the code for each route. Remix has the file-system based router similar to nextjs
-
-
public
folder contains the static files and assets that are served to the browser when our app is built or deployed.
👨💻 Building the project
Let’s start building the blog now. Let’s first clean up the app/routes/index.jsx
file.
app/routes/index.jsx
export default function Index() {
return (
<div style={{ fontFamily: 'system-ui, sans-serif', lineHeight: '1.4' }}>
<h1>Welcome to my blog</h1>
</div>
);
}
Remix supports the use of MDX to create a route module, which means we could create a new route using just a plain MDX file.
Let’s create a new directory inside the routes
directory called posts
and inside that directory let’s create a new file called first-blog-post.mdx
app/routes/posts/first-blog-post.mdx
Hey, welcome to my first blog post 👋
To check out your first blog post, visit localhost:3000/posts/first-blog-post
TADA 🎉, we have built a basic blog within 2 minutes
🙌 Adding frontmatter
The lines in the document above between the ---
are called “frontmatter”
Let’s add some front matter to your first blog post page. You can think frontmatter as the metadata of that page.
You can reference your frontmatter fields through the global attributes variable in your MDX.
---
title: First Blog Post
---
Hey, welcome to {attributes.title} 👋
Let’s now add metadata to our blog post’s page using frontmatter.
---
title: First Blog Post
meta:
title: First Blog Post
description: ✨ WoW
---
Hey, welcome to {attributes.title} 👋
As you can see the title of the page has been changed
… and the description as well
Let’s me quickly add a few blog posts
Umm… 🤔 Our blog isn’t completed yet with any kind of syntax highlighting ✨
✨ Adding syntax highlighting
We are going to be using highlight.js for syntax highlighting, you could even use prism.
🔌 About MDX plugins
We are going to achieve syntax highlighting using something called “MDX plugins”. By plugins, we could manipulate the process of MDX converting into HTML.
Generally, there are two types of plugins
-
Remark plugins are responsible for manipulating the process of converting MDX to markdown.
-
Rehype plugins are responsible for manipulating the process of converting the markdown to HTML.
For our remix blog, we are going to be using a rehype plugin called rehype-highlight. To install the package using the following command:
npm install rehype-highlight
We need to add a bit of configuration to the remix.config.js
file
mdx: async (filename) => {
const [rehypeHighlight] = await Promise.all([
import('rehype-highlight').then((mod) => mod.default),
]);
return {
rehypePlugins: [rehypeHighlight],
};
};
Now our remix.config.js
file would look something like this:
/**
* @type {import('@remix-run/dev/config').AppConfig}
*/
module.exports = {
appDirectory: 'app',
assetsBuildDirectory: 'public/build',
publicPath: '/build/',
serverBuildDirectory: 'api/_build',
ignoredRouteFiles: ['.*'],
mdx: async (filename) => {
const [rehypeHighlight] = await Promise.all([
import('rehype-highlight').then((mod) => mod.default),
]);
return {
rehypePlugins: [rehypeHighlight],
};
},
};
🧾 Creating a layout file
Now we have created a layout file, where we would import one of the highlight.js’s styling. I would be using night owl style, you could choose your style from highlight.js’s style demo page
To create a layout file for our blog posts, we have created a new file with the same name as the folder name (posts
) and the same level of the posts
folder.
Now we have to import the night owl theme into our layout file and use that as well.
import styles from 'highlight.js/styles/night-owl.css';
import { Outlet } from 'remix';
export const links = () => {
return [
{
rel: 'stylesheet',
href: styles,
},
];
};
export default function Posts() {
return <Outlet />;
}
In remix, we have the links function is similar to the links tag in HTML.
PS: If you are a VSCode user then install this remix run snippets extension 🚀.
Now let’s restart our local development server.
TADA 🎉, we have this wonderful syntax highlighting for our code blocks in our blog now
🎨 Adding TailwindCSS Typography
Right now our blog has syntax highlight but the font isn’t looking great 🤔 and there is nothing great than @tailwindcss/typography plugin to automatically styles our entire page’s using a single prose
class.
📦 Installing dependencies
We need a few dependencies for us to use tailwindcss and tailwindcss’s typography plugin.
Those dependencies are:
- Concurrently: Concurrently allows you to run multiple commands in a single terminal, so we can watch and build our tailwindcss styles as well as our entire remix application in a single terminal session
Let’s install all of them:
npm install -D tailwindcss concurrently @tailwindcss/typography
⚙ Configuring TailwindCSS
Create a new file named tailwind.config.js
, this file would contain all the configurations for tailwindcss.
Add the following configuration to the tailwind.config.js
file
tailwind.config.js
module.exports = {
mode: 'jit',
purge: ['./app/**/*.{ts,tsx}'],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [require('@tailwindcss/typography')],
};
We would have to change the scripts in package.json
"scripts": {
"build": "npm run build:css && remix build",
"build:css": "tailwindcss -o ./app/tailwind.css",
"dev": "concurrently \"npm run dev:css\" \"remix dev\"",
"dev:css": "tailwindcss -o ./app/tailwind.css --watch"
},
Importing tailwindcss into the app/root.jsx
file
app/root.jsx
import styles from './tailwind.css';
export const links = () => {
return [{ rel: 'stylesheet', href: styles }];
};
Let’s restart our server and run the npm run dev
command
You would see an error saying that
app/root.jsx:9:19: error: Could not resolve "./tailwind.css
This occurred because there is no tailwind.css
file but you would see that the file is been created. If in your case the file didn’t create then create a new file named tailwind.css
in the app
directory and copy and paste the CSS from this gist, https://gist.github.com/Kira272921/4541f16d37e6ab4d278ccdcaf3c7e36b
💻 Using @tailwindcss/typography plugin
Let’s open the app/routes/posts.jsx
file and add few styling.
As
app/routes/posts.jsx
file is the layout file for all the blog posts, if few add any kind of styling then it would reflect in the blog posts pages
return (
<div className='flex justify-center'>
<div className='prose lg:prose-xl py-10'>
<Outlet />
</div>
</div>
);
Here are using the @tailwindcss/typography
plugin
TADA 🎉. Look how beautiful the blog posts are looking now
📰 Creating a list of articles
Let’s create a list of articles on the main page (aka root route).
In remix, you could import the entire mdx module as well as the attributes within them.
app/index.js
import * as firstPost from './posts/build-a-cli-using-nodejs.mdx';
import * as secondPost from './posts/build-a-rest-api-using-nodejs.mdx';
The below function would return the slug (the file name, without the .mdx
) with the markdown attributes
app/index.jsx
function postFromModule(mod) {
return {
slug: mod.filename.replace(/\.mdx?$/, ''),
...mod.attributes.meta,
};
}
In remix, we use a loader function to load data on the server-side
app/index.jsx
export const loader = () => {
return [postFromModule(firstPost), postFromModule(secondPost)];
};
Here we are loading each of our MDX modules on the server-side using the loader function
Finally, our app/index.jsx
would look something like this
import { Link, useLoaderData } from 'remix';
import * as firstPost from './posts/build-a-cli-using-nodejs.mdx';
import * as secondPost from './posts/build-a-rest-api-using-nodejs.mdx';
function postFromModule(mod) {
return {
slug: mod.filename.replace(/\.mdx?$/, ''),
...mod.attributes.meta,
};
}
export const loader = () => {
return [postFromModule(firstPost), postFromModule(secondPost)];
};
export default function BlogIndex() {
const posts = useLoaderData();
return (
<div className='prose lg:prose-xl py-10 pl-10'>
<h2>Articles</h2>
<div className='flex justify-center'>
<ul>
{posts.map((post) => (
<li key={'posts/' + post.slug}>
<Link to={'posts/' + post.slug}>{post.title}</Link>
{post.description ? (
<p className='m-0 lg:m-0'>{post.description}</p>
) : null}
</li>
))}
</ul>
</div>
</div>
);
}
This is how our main page looks 🚀
🚀 Deploying to Vercel
As our application let’s deploy it on vercel 🚀.
-
Initialize an empty git repository
git init
-
Create a new GitHub repository
-
Push your changes to that repository
git remote add origin git@github.com:Kira272921/remix-blog.git # change URL to your repo's link
git add .
git commit -m "feat: initial commit"
git branch -M main
git push -u origin main
-
If you don’t have an account on vercel, create one
-
Create a new project
-
Import the remix application from our GitHub account
-
Deploy the application
-
If you are getting an error something like this, add a new script to
package.json
"postinstall": "remix setup node"
-
The entire code for this tutorial is present on my GitHub.
Here is what we have built today 🚀
🧑 About the author
So that’s it for this blog post folks 🤞. Meet y’all in the next blog.