Add Page Titles and the lang Attribute in a Next.js App

3 min read

Let's take care of some minor, but important, cleanup tasks. If we run an accessibility audit in lighthouse on our pages, we'll be notified that the html element doesn't have a lang attribute. The other minor item we'll be addressing in this installment in the series is the fact that our pages all share the same title.

Adding the lang Attribute

Our root html element should have a lang attribute to satisfy WCAG 2.1 Success Criteria 3.1.1. This will make our pages more accessible

Rather than taking over control of the document root with Next, we can provide this information with some configuration and let Next handle applying the lang attribute for us.

We'll need to add a next.config.js file to the root of our project. In that file, we'll export an object as a module and in that object, we'll add an i18n (internationalization) key.

module.exports = {
  i18n: {
    locales: ['en-US'],
    defaultLocale: 'en-US',

In this config, I've set the locale to en-US to support English as spoke in the United States, but this should be set to the default locale you'll be supporting.

This i18n config can support multiple languages with Next's Internationalized Routing, but that is beyond the scope of this series. For now, we'll take advantage of the default setting to add the lang attribute for accessibility purposes.

With this config in place, when we load up the site again (you may need to restart) and run a lighthouse audit, the warning for the missing lang attribute should be cleared up and if you view source on any of the pages, you should see that the attribute is being applied to the html.

Giving Posts Dynamic Page Titles

Now let's move on to giving our posts unique page titles. We'll keep our standard title and for a post, we'll append the title of the post. This will help a bit with SEO and ensure that if a post is bookmarked, it will clearly show what it is in the bookmark.

We'll do this using the Head component provided by Next in our pages/blog/[slug].tsx. We already have a Head component here to pull in our syntax highlighting CSS file, so we'll update this Head instance to add our dynamic title.

export default function Post({ slug, mdxSource, data }: PostProps) {
  const displayDate = new Date(data?.date).toDateString()
  return (
+       <title>My Awesome Blog - {data?.title || slug}</title>
      <div className="mt-4">

Here, we're adding a title element with our generic "My Awesome Blog" title along with the title from our post metadata. If that isn't defined for some reason, we fall back to adding the slug to the title.

Now, if we navigate to a blog post, the title should reflect the post title in the browser tab.


These were relatively small changes compared to some of what we've covered so far, but it doesn't make these elements any less important. Sometimes we can make big improvements for our users without a ton of effort.

Hot Tip
Check back next week for the next article in the series, Integrate the Next Link Component with Your MDX Content!