Next Intl Blog Template

Next Intl Blog Template banner Next Intl Blog Template banner




Recently I update this website, and as you may know, is an English and Spanish content website.

I'm not using a translation plugin, instead I write every work in both English and Spanish.

Thanks to Next.js and next-intl I can achieve this, rendering routes for each language in the website, accessing a dictionary that contains the content translated by me.

For the .mdx files, I created a directory for each language, and inside of those directories it contains the content in both languages too.

How to use

This template is an extension of next-intl, chek the getting started to learn the basics, the purpouse of the template is to create a simple layout for future customization.

Add or remove locales

You can add or remove locales in the src/lang/locales.ts file.

export type locales = 'en' | 'es';
export const localesList: locales[] = ['en', 'es'];

Just add or remove a locale from the locales const, and add or remove it from the list.

The first item in the localesList must be the default locale.

The list is used for static generation of the routes in src/app/[locale]/layout.tsx.

import { localesList } from '@/lang/locales';
export function generateStaticParams() {
  return => ({ locale }));

Remember to update the matcher in src/middleware.ts.

export const config = {
  matcher: ['/', '/(en|es)/:path*'],

And of course, update your src/lang/[locale].json files.

Content creation

Use src/content/[locale] for create content, in the /[locale]/ directory ceate the directory for each purpouse, for example: /[locale]/blog.

Inside create the .mdx file with an unique name, the name will be used as the slug for create the static page for that post.

For create a blog section, you'll use the getAllContent function in your route, for example: src/app/[locale]/blog/[slug]/page.tsx.

import { Mdx } from '@/components';
import { TParamsLocale, TPage, TSlugLang } from '@/types';
import { Metadata } from 'next';
import { getAllContent, getContent } from '@/utils/getContent';
export async function generateStaticParams(
  props: TParamsLocale,
): Promise<TSlugLang[]> {
  const blogs = await getAllContent(props.params.locale, 'blog');
  if (!blogs) return [];
  return => ({
    slug: blog.slug,
    locale: props.params.locale,

This will create each static page for each blog post.

You can get the metadata of the .mdx file too.

export async function generateMetadata(props: TPage): Promise<Metadata> {
  const blog = await getContent(props.params.locale, 'blog', props.params.slug);
  if (!blog) return {};
  return {
    title: blog.title,

Then, render the content using the Mdx component.

export default async function Page(props: TPage) {
  const post = await getContent(props.params.locale, 'blog', props.params.slug);
  if (!post) return null;
  return <Mdx code={post.body.code} />;

You can fork this template here

Posted: December 18, 2023