Welcome to the design-code.tips Next.js starter!
This open-source starter template is built with Next.js 14, Tailwind CSS, Contentlayer, and Decap CMS. It's designed to be a simple and customizable way to launch a modern blog, with support for MDX and multiple categories like Code Blog, Inspiration, Podcasts, Tools, and Resources.
<ExportedImage />
component from the next-image-export-optimizer
package for optimized image handling in static exports, replacing the default Next.js <Image />
component for better control over image quality, formats (like WEBP), and cache settings.Follow these steps to get the project up and running:
Clone the repository:
git clone https://github.com/ositaka/nextjs-blog-tailwind-starter
cd nextjs-blog-tailwind-starter
Install dependencies:
npm install
Run the development server:
npm run dev
Open http://localhost:3000 in your browser to see the app running.
You can create different types of content (like blog posts or podcasts) in separate folders under content/
. Each folder corresponds to a category in your blog.
Content is written in MDX format and managed using Contentlayer. To create a new post, add an .mdx
file in the appropriate folder inside content/
for each category:
content/
├── blog/
├── inspiration/
├── podcasts/
├── resources/
└── tools/
Each post supports frontmatter fields like title
, description
, date
, and featured
.
Example frontmatter for a blog post:
---
templateKey: blog
title: >
Custom Scrollbar with CSS (WebKit)
date: 2021-06-19T19:28:37.629Z
featured: true
description: >
Learn how to customize the scrollbar with CSS for WebKit browsers, providing a visually appealing design for scrollable elements on your website.
tags:
- web-development
- css
---
Your markdown content here...
Example inspiration post, with a video:
---
templateKey: inspiration
title: >
2Advanced Studios — Flash website in 2003
date: 2003-03-03T15:04:10.000Z
featured: true
description: >
An old flash website (v4) of 2advanced.com, that has inspired me quite a lot in my teenage years.
tags:
- animation
- flash
- website
image: /media/2advanced-flash-website-v4-2003.jpg
---
<Video src="/media/2advanced-flash-website-v4-2003.mp4" />
## © 2003 Advanced Studios
Currently, the 2Advanced Studios are closed for real, since some years already.
To use Decap CMS, you'll need to run its server alongside your Next.js development server. This can be done by running the following command in a separate terminal:
npx decap-server
You can then access the CMS at http://localhost:3000/admin/index.html.
[!NOTE]
If you'd like to test the CMS locally, setlocal_backend: true
inpublic/admin/config.yml
. Don't forget to restart the server after making changes.
[!TIP]
For more information, refer to the Decap CMS documentation. If you'd like to see an advanced example of how to use Decap CMS, check out this Decap CMSconfig.yml
example.
Customize the project to suit your needs by editing the following files:
config.js
: Your Bleg Starter configuration.tailwind.config.js
: Tailwind CSS configuration.next.config.js
: Next.js custom settings.next-seo.config.js
: SEO configuration for Next.js.contentlayer.config.js
: Contentlayer configuration for MDX files.public/admin/config.yml
: Decap CMS configuration.next.config.js
:next-image-export-optimizer
package to enhance image handling in static exports.public/media
folder.out/
folder.nextImageExportOptimizer_generateAndUseBlurImages
to false
in your .next.config.js
file, and pass placeholder="empty"
to all <ExportedImage>
components.[!NOTE]
Replace Next.js<Image />
components with<ExportedImage />
to leverage these optimizations.
Example usage:
import ExportedImage from 'next-image-export-optimizer'
<ExportedImage
src="/media/example.jpg"
alt="Example Image"
width={800}
height={600}
placeholder="blur"
/>
This project uses Contentlayer to automatically generate TypeScript types for your content. The configuration is managed in the contentlayer.config.ts
file located at the root of the project. Each document type (e.g., Blog, Inspiration, Podcasts, Tools, Pages) has its own structure and generated types, ensuring type safety when working with content in your components.
Below is an example of the TypeScript types generated for the Blog document:
import { defineDocumentType, makeSource } from 'contentlayer2/source-files'
const Blog = defineDocumentType(() => ({
name: 'Blog',
filePathPattern: `blog/*.md`,
contentType: 'markdown',
fields: {
title: { type: 'string', required: true },
date: { type: 'date', required: false },
description: { type: 'string', required: false },
tags: { type: 'json', required: false },
templateKey: { type: 'string', required: true },
featured: { type: 'boolean', required: false },
},
computedFields: {
slug: {
type: 'string',
resolve: (doc) => doc._raw.sourceFileName.replace(/\.md/, ''),
},
},
}))
export default makeSource({
contentDirPath: 'content',
documentTypes: [Page, Blog, Inspiration, Podcasts, Tools, Resources],
disableImportAliasWarning: true,
})
[!NOTE]
Theslug
field is automatically computed from the filename, removing the.md
extension.
The types are generated into the ./.contentlayer
directory and can be used throughout the application. Simply import the types from there:
import { allBlogs, Blog } from '../../.contentlayer/generated')
import { pick } from '@contentlayer2/client'
When working with content in your components, you can use the generated types to ensure type safety. For example, when mapping over blog posts:
let blogs = allBlogs.map((blog) => pick(blog, ['title', 'date', 'slug', 'description', 'templateKey'])
In this example, allBlogs
is an array of Blog
types, and we're using the pick
function to select specific fields from each blog post.
When working with content from allBlogs
, you can assert the type to ensure it's a Blog
type:
const blog = allBlogs.find((b) => b.slug === params.slug) as Blog
This way, you can access the fields of the Blog
type without any issues.
When passing content to components, you can use the Blog
type to ensure the correct structure:
<BlogPostCard key={post.slug} post={post as Blog} />
With these types in place, you can benefit from strong typing and auto-completion when working with your content.
Deploy your own instance of this blog starter project using one of the following providers:
[!NOTE]
If you encounter errors related tosharp
during deployment, please try removing thepackage-lock.json
file, as this can sometimes resolve issues with Sharp's dependencies.
If you're deploying your site with Netlify and using Decap CMS for content management, you'll need to enable Netlify Identity to allow users to log in to the CMS at /admin.
Enable Identity:
Configure Git Gateway:
Invite Users:
Login Access:
Here's a preview of how this might look in your config.yml
for Decap CMS:
backend:
name: git-gateway
branch: main
To enable Netlify Identity in this starter project, the following component is used in the layout.tsx
file:
<NetlifyIdentityRedirect />
This component automatically redirects users to the login page if they are not authenticated.
[!TIP]
For more information on setting up Netlify Identity with Decap CMS, visit the Decap CMS documentation – Choosing a Backend.
This project is licensed under the MIT License. See the LICENSE file for details.
Contributions are welcome! Here are some ways you can help improve the project:
git checkout -b feature/your-feature-name
)git push origin feature/your-feature-name
)Thank you for considering contributing to the project!
Built with ❤️ in 🇵🇹 and 🇧🇪 using Next.js (an amazing open-source React framework), Tailwind CSS (for rapid UI development), Contentlayer (for managing content), and Decap CMS (for a user-friendly content management experience).
A big thank you to the communities behind these projects for their hard work and dedication!