Migrating My Blog from Jekyll to Next.js and Tailwind CSS
A modern web development workflow for blogging, Introducing Tailwind Nextjs Starter Blog
After a few years of on , I decided it was time for an upgrade. Jekyll served me well, but as my needs evolved, I found myself wanting more flexibility, better performance, and a more modern development workflow. Enter , , and a host of other modern web development tools that have transformed my blogging experience. Here's a rundown of the migration process and the benefits I’ve enjoyed since making the switch.
Jekyll is a static site generator that is simple, reliable, and supported by GitHub Pages. However, it has its limitations:
- Performance: While Jekyll is fast for simple blogs, it can become sluggish as your content grows.
- Customization: Advanced customization often requires diving into Liquid templates and custom plugins, which can be cumbersome.
- Modern Workflow: Jekyll lacks some modern development features like hot-reloading and advanced state management.
I decided to use a more "modern"stack with Next.js, which is a React-based framework that offers the perfect balance of flexibility and performance with the following features:
- and : Next.js allows you to choose between SSR and SSG, optimizing performance and SEO.
- : Easy creation of dynamic routes without complex configuration.
- API Routes: Built-in API routes simplify backend logic integration.
Tailwind Nextjs Starter Blog
I believe in reusing existing solutions to save time and effort, and a quick search led me to an amazing starting point: the . This starter template provided a solid foundation, integrating Next.js and Tailwind CSS with additional features like MDX support, theme switching, and pre-configured styles.
This is a 13+, blogging starter template. Probably the most feature-rich Next.js markdown blogging template out there. Comes out of the box configured with the latest technologies to make technical writing a breeze. Easily configurable and customizable. Perfect as a replacement to existing Jekyll and Hugo individual blogs. The main features that I relied and extended on are:
- Easy styling customization and Mobile-friendly view with and primary color attribute
- for managing content
- Server-side syntax highlighting with line numbers and line highlighting via
- Math display supported via
- Citation and bibliography support via
- Automatic image optimization via
- Flexible data retrieval with
- Preconfigured security headers
- SEO friendly with RSS feed, sitemaps
The Template Structure
First, lets understand the different folders/components of the template:
static: Next.js can serve static files, like images, under a folder called public in the root directory. Files insidepubliccan then be referenced by your code starting from the base URL/. For our template, we have astaticfolder that contains images to be used in the blog, logos,icons and favicons.scripts: This folder contains scripts to help with the development and maintenance of the blog.- The
build.mjsscript is used to perform a bunch of steps post theyarn run buildcommand. It is used to generate the RSS feed and the tags and categories pages. - The
rss.mjsscript is used to generate the RSS feed for the blog.
- The
-
data: Thedatafolder contains the site metadata, authors, and blog posts in.mdx. ThemarkdownToHtmlfunction is used to convert markdown.mdxto HTML.authors: contains the author information for the blog posts and is used to populate theaboutpage. This can be extended to allow multiple authors.blog: contains the blog posts inmdxformat. The folders are used to categorize the blog posts.metadata: contains the site metadata like navigation links, site title, description, etc. We will discuss this in more details in the metadata section
-
layouts: Thelayoutsfolder contains the main templates used in the pages. The main layout isMainLayout.jswhich is used to wrap all the pages. ThePostLayout.jsis used to wrap all the blog posts. -
lib: Thelibfolder contains utility functions and configurations used throughout the blog.contentLayer: contains the content layer "plugins"/"scripts" for the blog posts and projects.computedFields.js: contains the computed fields for the blog posts. Those are fields that are generated based on the content of the blog post or its metadata like thereadingTimeortoc(table of contents).contentFields.js: defined the fields that appear in the frontmatter for the different types of content (author, post, project, etc.).generatedCategories.mjs: Loops through all the posts and generates the categories for the blog posts.generatedTags.mjs- generates the tags for the blog posts.projectFields.js- contains the project fields for the projects page.structuredData.js- contains the structured data for the blog posts.
-
mdx- contains the utility functions for the MDX files.remark-code-title.js- adds a title to the code blocks.rehype-citation.js- adds citation support to the MDX files.rehype-katex.js- adds math display support to the MDX files.
-
packages/design-system: The local design-system package contains the reusable React components, MDX components, design tokens, Tailwind preset, global CSS entrypoint, and Storybook stories used by the blog. -
app: Theappfolder contains the main application routes and application shell. Shared UI is imported from the design-system package instead of living in a root-level components folder.
Quick Start Guide
- Try installing the starter using the new :
npm i -g @pliny/cli
pliny new --template=starter-blog my-blogIt supports the updated version of the blog with Contentlayer, optional choice of TS/JS and different package managers as well as more modularized components which will be the basis of the template going forward.
Alternatively to stick with the current version, TypeScript and Contentlayer:
npx degit 'timlrx/tailwind-nextjs-starter-blog#contentlayer'or JS (official support)
npx degit https://github.com/timlrx/tailwind-nextjs-starter-blog.git- Personalize
siteMetadata.js(site related information) - Modify the content security policy in
next.config.jsif you want to use any analytics provider or a commenting solution other than giscus. - Personalize
authors/default.md(main author) - Modify
projectsData.js - Modify
headerNavLinks.jsto customize navigation links - Add blog posts
- Deploy on Vercel
Development
First, run the development server:
npm start
# or
npm run devOpen http://localhost:3000 with your browser to see the result.
You can start editing the page by modifying pages/index.js. The page auto-updates as you edit the file.
Extend / Customize
data/siteMetadata.js - contains most of the site related information which should be modified for a user's need.
data/authors/default.md - default author information (required). Additional authors can be added as files in data/authors.
data/projectsData.js - data used to generate styled card on the projects page.
data/headerNavLinks.js - navigation links.
data/logo.svg - replace with your own logo.
data/blog - replace with your own blog posts.
public/static - store assets such as images and favicons.
packages/design-system - contains the local design-system package, including reusable components, tokens, Tailwind preset, Storybook stories, and global design-system CSS exports.
tailwind.config.js - consumes the design-system Tailwind preset and scans the app and design-system package for classes.
packages/design-system/src/global.css - design-system global entrypoint imported by the app. It owns Tailwind and base browser compatibility only; component and MDX rendering styles live beside their owning design-system components.
packages/design-system/src/components/mdx/index.js - exposes the JSX components available inside .mdx and .md content.
layouts - main templates used in pages.
pages - pages to route to. Read the for more information.
next.config.js - configuration related to Next.js. You need to adapt the Content Security Policy if you want to load scripts, images etc. from other domains.
Post
Frontmatter
Frontmatter follows .
Currently 7 fields are supported.
title (required)
date (required)
tags (required, can be empty array)
lastmod (optional)
draft (optional)
summary (optional)
images (optional, if none provided defaults to socialBanner in siteMetadata config)
authors (optional list which should correspond to the file names in `data/authors`. Uses `default` if none is specified)
layout (optional list which should correspond to the file names in `data/layouts`)
canonicalUrl (optional, canonical url for the post for SEO)
Here's an example of a post's frontmatter:
---
title: 'Introducing Tailwind Nexjs Starter Blog'
date: '2021-01-12'
lastmod: '2021-01-18'
tags: ['next-js', 'tailwind', 'guide']
draft: false
summary: 'Looking for a performant, out of the box template, with all the best in web technology to support your blogging needs? Checkout the Tailwind Nextjs Starter Blog template.'
images: ['/static/images/canada/mountains.jpg', '/static/images/canada/toronto.jpg']
authors: ['default', 'sparrowhawk']
layout: PostLayout
canonicalUrl: https://tailwind-nextjs-starter-blog.vercel.app/blog/introducing-tailwind-nextjs-starter-blog
---
Compose
Run node ./scripts/compose.js to bootstrap a new post.
Follow the interactive prompt to generate a post with pre-filled front matter.