tutorial

How to easily add a favicon to a Next.js app

Adding a favicon to a website enhances usability and branding. It helps identify the website in browser tabs and bookmarks, improving user recognition and trust.

Let’s learn how to quickly add a favicon image to a Next.js app

In this article

Add favicon automatically in Next.js 13 App Router

To add a favicon in Next.js 13 App Router, add a favicon.ico file to the app/ directory. Next.js will automatically detect favicon.ico and display it on the page.

Here we’ve added the favicon.ico to our VS Code Next.js project.

favicon.ico is the app directory in Visual Studio Code.

And this is all we need to do – here’s layout.tsx:

src/app/layout.tsx
import { Metadata } from 'next'; import './globals.css'; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="en"> <body>{children}</body> </html> ); }

And we’ll instantly see the image next to the page title in the browser tab:

The favicon shows up next to the page title in the browser-tab

Apart from favicon and .ico, Next.js also auto-discovers the following file name and extension combinations in the app/ directory

  • icon with .ico, .jpg, .jpeg, .png, or.svg extension.
  • app-icon with .jpg, .jpeg, .png extension.

You can rename PNGs to .ico files and they will still work.

Add favicon automatically in Next.js Pages Router

To add a favicon automatically in the Next.js pages directory, place a favicon.ico file in your public/ directory, and your browser will automatically detect the file for the site icon.

favicon.ico is the public directory in Visual Studio Code.

If your image file isn’t a .ico, you’ll need to either rename it to favicon.ico, or manually specify the filename.

Set Next.js favicon statically in Next.js 13 App Router

To add a favicon to a Next.js 13 app, you can also export a Metadata object with an icons property in your layout.tsx file:

src/app/layout.tsx
import { Metadata } from 'next'; import './globals.css'; export const metadata: Metadata = { icons: { icon: '/icon.png', }, }; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="en"> <body>{children}</body> </html> ); }

The image file should be in your public directory.

Set Next.js favicon with HTML in Pages Router

To add a site icon in the Next.js pages or app directory, use the HTML <link> tag in your _document.tsx or _document.js file, just like in vanilla HTML:

pages/_document.tsx
import { Html, Head, Main, NextScript } from 'next/document'; export default function Document() { return ( <Html lang="en"> <Head> <link rel="icon" href="/favicon.png" /> </Head> <body> <Main /> <NextScript /> </body> </Html> ); }

The favicon should be in your public directory.

Add favicon with HTML in Next.js 13 App Router

You can also use the HTML <link> tag to add a favicon in a project using the Next.js app directory, in your layout.tsx file:

src/app/layout.tsx
import './globals.css'; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="en"> <head> <link rel="icon" href="/app-icon.jpg" /> </head> <body>{children}</body> </html> ); }

The favicon should be in your public directory.

How to add or toggle a class on the body element in JavaScript

Adding a class to the <body> tag can be useful for applying global styles or targeting specific elements within the body for styling or functionality.

This article will teach us how to easily add or toggle a class on the HTML body element using JavaScript.

In this article

To add a class to the HTML body element in JavaScript on page load, call the classList.add() method on it, i.e., document.body.classList.add(element).

HTML
<!DOCTYPE html> <html> <head> <title>Coding Beauty Tutorial</title> </head> <body class="dev coding"> <div>This is a div element.</div> </body> </html>
JavaScript
document.body.classList.add('class-3');

The body property is the HTMLElement object that represents the body tag in the markup.

The classList property is a DOMTokenList object that represents the list of classes an element has.

The add() method of the classList property takes a list of classes and adds them to an element.

JavaScript
<!DOCTYPE html> <html> <head> <title>Coding Beauty Tutorial</title> </head> <body class="dev coding beauty"> <div>This is a div element.</div> </body> </html>

You can pass multiple arguments to add() to add more than one class to the body. For example, we can add both beauty and magic to the body in single statement.

JavaScript
document.body.classList.add('beauty', 'magic');

To produce this HTML markup:

JavaScript
<!DOCTYPE html> <html> <head> <title>Coding Beauty Tutorial</title> </head> <body class="dev coding beauty magic"> <div>This is a div element.</div> </body> </html>

If you add class that already exists on the HTML body, add() ignores the class instead of throwing an exception.

Add class to body tag in <head> tag

To add a class to the body element in the <head> tag using JavaScript, use the DOMContentLoaded event and the document.body.classList.add() method.

For example:

JavaScript
<!DOCTYPE html> <html> <head> <title>Coding Beauty Tutorial</title> <script> document.addEventListener('DOMContentLoaded', () => { document.body.classList.add('beauty', 'magic'); }); </script> </head> <body class="dev coding"> <div>This is a div element.</div> <script src="index"></script> </body> </html>

The DOMContentLoaded event runs when the HTML is completely parse and the DOM has loaded.

The <script> loads and the JavaScript runs before the browser renders the HTML, so without DOMContentLoaded, document.body will be null in the <script>, causing the “Cannot read property ‘classList’ of undefined” JavaScript error.

The "Cannot read property 'classList' of undefined" error happening in JavaScript.

Add class to body tag on click

If you’d like to add a class to the body tag when the user clicks an element, set a click listener on element and call document.body.classList.add() in this listener.

For example:

HTML
<!DOCTYPE html> <html> <head> <title>Coding Beauty Tutorial</title> </head> <body class="dev coding"> <div>So you can code</div> <button class="amazify">Be amazing</button> </body> </html>
JavaScript
const button = document.getElementById('amazify'); button.addEventListener('click', () => { document.body.classList.add('amazify'); });

Toggle class on body element

Toggling a class on the body element in JavaScript simplifies code implementation by handling the addition and removal of the class in a single line of code.

And this single line of code is a call to the body’s classList.toggle() method:

JavaScript
document.body.classList.toggle('incredible');

As you would expect, the class is removed from the body when it’s already there and added when it isn’t.

Next.js: How to set page title and meta description

Setting the page title and meta description helps improve search engine visibility, increase click-through rates, and provide concise summaries of webpage content.

In this article, we’re going to learn how we easily set the page title and meta description in a Next.js project.

In this article

Set static page title and meta description in Next.js App Router

To set page title and meta description statically in Next.js 13’s app directory, create a Metadata object in a page.tsx file and export it:

src/app/page.tsx
import React from 'react'; import { Metadata } from 'next'; export const metadata: Metadata = { title: 'Coding Beauty', description: 'codingbeautydev.com: Coding - the art, the science, and the passion.', }; export default function Page() { return ( <main> <h1>Welcome to Coding Beauty</h1> </main> ); }

The tab will have this title, and the page will have a meta tag containing this description:

A Next.js page with a title and meta description.

We can also do this in a layout.tsx to make every page using this layout have this title and meta description by default – if the page doesn’t have its own.

src/app/layout.tsx
import { Metadata } from 'next'; import '@/styles/globals.css'; export const metadata: Metadata = { title: 'Coding Beauty', description: 'The official Coding Beauty home page.', icons: { icon: '/favicon.png', }, }; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="en"> <body>{children}</body> </html> ); }

Set title and meta description dynamically in Next.js App Router

You can use the generateMetadata() function to set the page title and meta description using dynamic information we don’t know beforehand, like data from an API.

TypeScript
export function generateMetadata({ params }: Props): Promise<Metadata> { // fetch data with `params` // return a `Metadata` object return { title: post.name, description: post.description, } }

Let’s look at a full example, where we fetch API data for a post in our hypothetical social media app, and we use generateMetadata() to set the page title and meta description for the page based on what we get from the endpoint URL.

src/pages/posts/[id]/page.tsx
import { Metadata } from 'next'; type Props = { params: { id: string }; }; export async function generateMetadata({ params, }: Props): Promise<Metadata> { const id = params.id; const url = `https://api.mysocialapp.com/posts/${id}`; const post = await fetch(url).then((res) => res.json()); return { title: post.title, description: post.description, }; } export default async function Page({ params }: Props) { const { id } = params; const url = `https://api.mysocialapp.com/posts/${id}`; // fetch again! // But don't worry, Next.js caches the `fetch()` calls const post = await fetch(url).then((res) => res.json()); return ( <> <h1>{post.title}</h1> <div>{post.content}</div> </> ); }

We fetch data from the same endpoint twice, one for the title and meta description, and another to display the title and other information to the actual users on the page.

Next.js caches the results of the fetch in generateMetadata() for use in Page to prevent multiple requests and avoid performance hits.

If you export a Metadata object and also have generateMetadata() function, Next.js will use generateMetadata()‘s return value.

Set page title and meta description in Next.js Pages Router (<= 12)

To set the page title and meta description in the Next.js pages directory, create a Head component, and place meta and title tags in it, like you would in vanilla HTML:

pages/index.tsx
import Head from 'next/head'; export default function Home() { return ( <> <Head> <title>Coding Beauty</title> <meta name="description" content="A brand all about coding passion and success" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="icon" href="/favicon.png" /> </Head> <main> <h1>Welcome to Coding Beauty</h1> </main> </> ); }

We can also set the page title and meta description in your _app.tsx or _app.jsx file to make each page without a title or description use this as a default.

pages/_app.tsx
import '@/styles/globals.css'; import type { AppProps } from 'next/app'; import Head from 'next/head'; export default function App({ Component, pageProps }: AppProps) { return ( <> <Head> <title>Coding Beauty</title> <meta name="description" content="A page on the Coding Beauty website" /> </Head> <Component {...pageProps} /> </> ); }

Set page title and meta description dynamically in Next.js Pages Router (<= 12)

You can also set the page title and meta description in the pages directory based on some data not known ahead of time.

For instance, to set the meta information based on API data, you’ll fetch the data in getServerSideProps and display it in the title and meta tags that are in the Head tag.

pages/[id].tsx
import { GetServerSideProps } from 'next'; import Head from 'next/head'; export const getServerSideProps: GetServerSideProps = async (context) => { const { params } = context; const { id } = params!; const result = await fetch(`api.mystore.com/books/${id}`); const { title, description, details } = await result.json(); return { props: { title, description, details, }, }; }; export default function Home(props: any) { const { title, description, details } = props; return ( <> <Head> {/* ๐Ÿ‘‡ set page title and meta description dynamically */} <title>{title}</title> <meta name="description" content={description} /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="icon" href="/favicon.png" /> </Head> <main> <h1>{title}</h1> <p>{details}</p> </main> </> ); }

How to create a type from type or object keys or values in TypeScript

In this article

Create type from object keys in TypeScript

To create a type from an object’s keys in TypeScript, use the keyof typeof object. For example:

TypeScript
const brand = { name: 'Coding Beauty', domain: 'codingbeautydev.com', color: 'blue', }; // ๐Ÿ‘‡ type Keys = 'name' | 'domain' | 'color'; type BrandKeys = keyof typeof brand;

With this, you’ll have a type that only accepts strings matching the key name, and your code editor’s intellisense should indicate:

The new type only accepts values matching the object's keys.
The new type only accepts values matching the object’s keys.

Create type from another type’s keys in TypeScript

We use typeof because brand is an instance object, not a type. If it was a type, we would omit typeof:

TypeScript
type Brand = { name: string; domain: string; color: string; }; // ๐Ÿ‘‡ type Keys = 'name' | 'domain' | 'color'; type BrandKeys = keyof Brand;

As before, you’ll have a type that only contains strings matching the key name, and your code editor should detect this:

The new type only accepts values matching the first type's keys.
The new type only accepts values matching the first type’s keys.

Create type from object values in TypeScript

We can use typeof and type indexing to easily create a type from an object’s values in TypeScript:

TypeScript
const site = { url: 'codingbeautydev.com', color: 'blue', topic: 'coding', } as const; // ๐Ÿ‘ˆ const assertion // ๐Ÿ‘‡ type Values = 'codingbeautydev.com' | 'color' | 'coding' type Values = (typeof site)[keyof typeof site];

We this, we’ll have a type that only accepts strings matching the values of the object:

The new type only accepts values matching the object's values.
The new type only accepts values matching the object’s values.

Otherwise, there’ll be a TypeScript error

You can only values matching the object's values to the new type.

Create generic type from object values in TypeScript

The as const is called a const assertion in TypeScript. It tells the compiler to infer the most specific type possible from an expression. Without it, the inferred value type will be a primitive, like string or number or union of these primitives – string | number for example.

TypeScript
const site = { url: 'codingbeautydev.com', color: 'blue', topic: 'coding', }; // ๐Ÿ‘‡ type Values = 'string' type Values = (typeof site)[keyof typeof site]; // ๐Ÿ‘‡ No error - `Values` takes any string const testObj: Values = 'random string';

Values is no different from the string type here, and VS Code confirms it:

The Values type is a string.
The Values type is a string.

If there are multiple primitive types, the resulting generic type will be a union of all those primitives:

TypeScript
const site = { url: 'codingbeautydev.com', age: 1000, // ๐Ÿ‘ˆ also `number` type now topic: 'coding', }; // ๐Ÿ‘‡ type Values = 'string' | number type Values = (typeof site)[keyof typeof site]; // ๐Ÿ‘‡ No error const testObj: Values = 34124;
The Values type is a string | number type.
The Values type is a string | number type.

Create generic type from another type’s values in TypeScript

You can also create generic types from another type’s values in TypeScript, like this:

TypeScript
type Site = { url: 'codingbeautydev.com'; age: number; topic: string; }; // ๐Ÿ‘‡ type Site = 'string' | 'number' type Values = Site[keyof Site];
The Values type comes from the Site type's values.
The Values type comes from the Site type’s values.

How to allow all domains for images in Next.js

To allow Next.js to display images from all domains, modify your next.config.js file to add an object with protocol and hostname properties to the remotePatterns array property.

next.config.js
/** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, // other properties... remotePatterns: [ { protocol: 'https', hostname: '**', }, ], }; module.exports = nextConfig;

Why would I need to allow all domains for Next.js images?

Because you want to avoid this error:

The "hostname is not configured under images in your next.config.js" error occuring in a Next.js application.
Using a non-matching domain results in this error.

Which happens when you have next.config.js with a limited number of specified domains, for example:

Only images from upload.wikimedia.org are allowed in this Next.js app.
Only images from upload.wikimedia.org are allowed in this Next.js app.

But you try to display images from a domain not in the image.domains list:

src/app/blog/page.tsx
import React from 'react'; import { Metadata } from 'next'; import Image from 'next/image'; export const metadata: Metadata = { title: 'Coding Beauty', description: 'Tutorials by Coding Beauty', }; export default function Page() { return ( <main> <Image src="https://codingbeautydev.com/wp-content/uploads/2023/08/vscode-tips-tricks.png" alt="Coding Beauty article featured image" width={100} height={100} ></Image> 10 essential VS Code tips & tricks for greater productivity </main> ); }

remotePatterns vs images.domains property in next.config.js

In this case, you can follow the steps earlier in this article:

next.config.js
/** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, // other properties... remotePatterns: [ { protocol: 'https', hostname: '**', }, ], }; module.exports = nextConfig;

Or you can modify your next.config.js to include codingbeautydev.com:

Images can now also be displayed from codingbeautydev.com in this Next.js app.
Images can now also be displayed from codingbeautydev.com in this Next.js app.

Of course, if you don’t have any domains property, you just create one with the new domain as its single element.

While the image.domains is simple and straightforward, the remotePatterns property gives us more flexibility with its support for wildcard pattern matching, and protocol, port, and pathname restriction.

These advanced restrictions remotePatterns provide greater security when you don’t own all the content in the domain, which is why the Next.js team recommends it.

Why do we need to be explicit with domains for Next.js images?

So whether you use remotePatterns or images.domains, you still need to be explicit about the domains that a next/image component can display.

By doing so, you can prevent the potential security risks of loading images from random, unverified URLs that could contain malicious code.

How to easily get the intersection of two sets in JavaScript

To get the intersection of two Sets in JavaScript, combine the filter() and has() array methods:

JavaScript
const set1 = new Set([1, 2, 3, 4, 5]); const set2 = new Set([4, 5, 6, 7, 8]); const intersection = new Set([...set1].filter(x => set2.has(x))); console.log(intersection); // Set {4, 5}

Theย Arrayย filter()ย method tests each element in an array against a condition specified by a callback function and creates a new array filled with elements that pass the test. It doesnโ€™t modify the original array.

JavaScript
const arr = [1, 2, 3, 4]; const filtered = arr.filter((num) => num > 2); console.log(filtered); // [ 3, 4 ]

The spread syntax (...) converts the Set to an array:

JavaScript
const set1 = new Set([1, 2, 3, 4, 5]); const arrayFromSet = [...set1]; console.log(arrayFromSet); // [1, 2, 3, 4, 5]

We can also do this with the Array from() method, which converts Sets and other iterables into arrays:

JavaScript
const set1 = new Set([1, 2, 3, 4, 5]); const arrayFromSet = Array.from(set1); console.log(arrayFromSet); // [1, 2, 3, 4, 5]

And the Set has() method tells us whether or not the Set contains a particular element.

JavaScript
const set = new Set(['coding', 'beauty', 'dev']); const hasElegance = set.has('elegance'); const hasBeauty = set.has('beauty'); console.log(hasElegance); // false console.log(hasBeauty); // true

Get intersection of two sets with Array forEach() method

Alternatively, we can use the Array forEach() method to get the intersection of two Sets in JavaScript.

JavaScript
const set1 = new Set([1, 2, 3, 4, 5]); const set2 = new Set([4, 5, 6, 7, 8]); const intersection = new Set(); set1.forEach(value => { if (set2.has(value)) { intersection.add(value); } }); console.log(intersection); // Set {4, 5}

The Set forEach loops through an array and calls a specified function on each element.

JavaScript
const fruits = new Set(['apple', 'orange', 'banana']); fruits.forEach((fruit) => { console.log(fruit.toUpperCase()); }); // Output ๐Ÿ‘‡ // APPLE // ORANGE // BANANA

The Set add() method adds a new element to a Set:

JavaScript
const set = new Set([1, 2, 3]); console.log(set); // Set(3) { 1, 2, 3 } set.add(10); console.log(set); // Set(4) { 1, 2, 3, 10 }

Get intersection of two Sets with for..of loop

Anywhere you see the forEach(), you can use the for..of loop in its place:

JavaScript
const set1 = new Set([1, 2, 3, 4, 5]); const set2 = new Set([4, 5, 6, 7, 8]); const intersection = new Set(); for (const value of set1) { if (set2.has(value)) { intersection.add(value); } } console.log(intersection); // Set {4, 5}

Get intersection of two Sets with reduce() method

You can also use the reduce() method to get the intersection of two Set objects in JavaScript:

JavaScript
const set1 = new Set([1, 2, 3, 4, 5]); const set2 = new Set([4, 5, 6, 7, 8]); const intersection = [...set1].reduce((acc, value) => { if (set2.has(value)) { acc.add(value); } return acc; }, new Set()); console.log(intersection); // Set {4, 5}

The reduce() method calls a function on each element of an array to accumulate a resulting value:

JavaScript
const nums = [1, 2, 3, 4, 5]; const sum = nums.reduce((acc, num) => acc + num, 0); const product = nums.reduce((acc, num) => acc * num, 1); console.log(sum); // 15 console.log(product); // 120

How to quickly toggle (enable/disable) autosave in VS Code

To enable or disable autosave in VS Code, use the File > Auto Save option from the menu bar.

The `File > Auto Save` option in Visual Studio Code enables/disables autosave.

After this, the “dirty” indicator will no longer show up when you modify a saved file.

Before:

Visual Studio Code without autosave.
No autosave.

After:

Visual Studio Code with autosave.
Autosave enabled – the unsaved indicator no longer shows.

Why do we need autosave?

So we can stop mindlessly pressing Ctrl + S.

So we can save time, increase productivity, and be much more certain that we’re working with the latest changes.

Autosave is not perfect though; it’s up to you to weigh the pros and cons – which we comprehensively cover here.

What’s not to like about autosave?

Some of the cons we talk about in that autosave article:

  1. It wastes resources because it saves even when you’re not ready to view the results of your changes.
  2. It makes it harder to recover from unexpected errors, for example, making a buggy file change and accidentally closing the file.
  3. There’s no auto-formatting with auto-save.

Enable/disable autosave in VS Code Settings

Alternatively, we can disable autosave in Visual Studio Code using the Files: Auto Save setting in the Settings page.

You can easily navigate to this page with the gear icon at the top-left of the code editor:

You can open Settings with the bottom-left gear icon.

Once you get there, you can use the search bar to find the setting.

Using the `Files: Auto Save` option in VS Code Settings.

As you can see, Files: Auto Save can be one of four possible values, namely:

  1. off – self-explanatory: disable autosave.
  2. afterDelay: – the new value enables autosave with the File > Auto Save setting. autosaves the file sometime after your changes.
  3. onFocusDelay – autosaves the dirty file when you switch windows or tabs.
  4. onWindowChange – as the name implies, autosaves the unsaved file when you switch windows in the operating system.

So there are more customization options in the Settings page than in the menu bar.

Change autosave delay in VS Code

When Files: Auto Save is set to afterDelay, you can modify the autosave delay in Visual Studio Code with the Files: Auto Save Delay setting.

Modifying the autosave delay in VS Code.

You may be better off increasing the autosave delay instead of disabling autosave entirely, so VS Code still saves your work automatically, while minimizing the impact on system resources.

Enable/disable autosave in VS Code with Command Palette

To turn autosave on or off in Visual Studio Code, you can also use the File: Toggle Auto Save command, accessible from the Command Palette:

Toggling auto save in VS Code with the Command Palette.

How to quickly listen for a route/page change in Next.js

To detect a route change in Next.js:

  • app directory: use useEffect and usePathname.
  • pages directory: use useEffect and useRouter.

Listen for route/page change in Next.js app directory

To listen for a route change in Next.js 13 app directory, use the useEffect and the usePathname hooks. The action in useEffect will run anytime the pathname from usePathname changes.

src/app/route-change-listener.tsx
'use client'; import { usePathname } from 'next/navigation'; import { useEffect, useState } from 'react'; export function RouteChangeListener() { const pathname = usePathname(); const [changes, setChanges] = useState(0); useEffect(() => { console.log(`Route changed to: ${pathname}`); setChanges((prev) => prev + 1); }, [pathname]); return <></>; }

Here we log to the console and change the state of the component when the route changes.

The URL change listener logs to the console when the route changes.
The URL change listener logs to the console when the route changes.

Page change listener must persist with client-side routing

For this detection to work, the component containing this useEffect needs to be somewhere in the DOM where it will not get unmounted with client-side navigation.

In the Next.js 13 app directory, this could be the layout.tsx file:

src/app/layout.tsx
import { Metadata } from 'next'; import '@/styles/globals.css'; import { RouteChangeListener } from './route-change-listener'; export const metadata: Metadata = { title: 'Coding Beauty', description: 'The official Coding Beauty home page.', icons: { icon: '/favicon.png', }, }; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="en"> {/* ๐Ÿ‘‡ Persists with client-side navigation */} <RouteChangeListener /> <body>{children}</body> </html> ); }

useEffect needs 'use client'

Also, since server components are the default in Next.js 13, you’ll need to add the 'use client' directive at the top of the component file.

Otherwise, you won’t be able to use interactive client-side features like React hooks, including useEffect.

The useEffect React hook can't work without the 'use client' directive in Next.js.
useEffect can’t work without ‘use client’ in Next.js.

Listen for route/page change in Next.js pages directory

To handle a URL or location change in Next.js pages directory, combine the useEffect and the useRouter hooks:

src/pages/url-change-listener.tsx
import { useEffect } from 'react'; import { useRouter } from 'next/router'; export function UrlChangeListener() { const router = useRouter(); useEffect(() => { console.log(`The page is now: ${router.pathname}`); }, [router]); return <></>; }

Route change detector must persist with client-side routing

Just like with the app directory, the component that listens for the URL change with useEffect needs to be somewhere in the DOM where it will not get unmounted with client-side navigation.

In the pages directory, this could be the _app.tsx or _app.js file:

src/pages/_app.tsx
import '@/styles/globals.css'; import type { AppProps } from 'next/app'; import { UrlChangeListener } from './url-change-listener'; export default function App({ Component, pageProps }: AppProps) { return ( <> <UrlChangeListener /> <Component {...pageProps} /> </> ); }

Detect route/page with useRouter() events

Alternatively, we can detect a client-side URL change in the Next.js pages directory with events that the useRouter() object emits. For example:

src/pages/url-change-listener.tsx
import { useEffect } from 'react'; import { useRouter } from 'next/router'; export function UrlChangeListener() { const router = useRouter(); useEffect(() => { const startHandler = () => { console.log('Router change started'); }; const completeHandler = () => { console.log('Router change completed'); }; router.events.on('routeChangeStart', startHandler); router.events.on('routeChangeComplete', completeHandler); return () => { router.events.off('routeChangeStart', startHandler); router.events.off('routeChangeComplete', completeHandler); }; }, []); // ๐Ÿ‘‡ You can put a progress bar or something here return <></>; }

Here we used two important router events:

  • routeChangeStart – fires when the route is about to change.
  • routerChangeComplete – fires when the router has changed completely.

There’s more too, and their names are just as self-explanatory as these two.

The `useRouter()` object has other properties apart from routeChangeStart and routeChangeComplete.

Key takeaways

  • To listen for a route or page change in Next.js app directory, combine the useEffect and usePathname hooks.
  • Detect a URL change in the pages directory with the useEffect and useRouter hooks.
  • You can also use the routeChangeStart and routeChangeComplete events from the useRouter() object to handle a location change.

How to quickly fix the “Cannot read property ‘then’ of undefined” error in JavaScript

The “Cannot read property ‘then’ of undefined” error occurs in JavaScript when you call try then() on a value – typically a function’s return value – but this value is undefined.

The "Cannot read property 'then' of undefined" error occurring in the VS Code terminal.
The “Cannot read property ‘then’ of undefined” error occurring in the VS Code terminal.

This could happen for one of the following reasons:

  1. Not returning the Promise in the function with return.
  2. Not returning a value in an async function.
  3. Not chaining Promises properly.

To fix it, ensure the function returns actually returns a Promise.

Now let’s look at some specific cases of the error and learn how to fix it in a few easy steps.

Fix: Promise not returned

The “Cannot read property ‘then’ of undefined” happens when you forget to use the return keyword return the Promise from the function.

JavaScript
function fetchData(apiUrl) { // ๐Ÿ‘‡ `return` keyword missing fetch(apiUrl).then((response) => { return response.json(); }); } // โŒ Cannot read property 'then' of undefined fetchData('/api/data') .then((data) => console.log(data))

To fix it, simply return the Promise with return:

JavaScript
function fetchData(apiUrl) { // we return the Promise return fetch(apiUrl).then((response) => { return response.json(); }); } // โœ… Runs successfully fetchData('/api/data') .then((data) => console.log(data))

Fix: Asynchronous function doesn’t return a value

The “Cannot read property ‘then’ of undefined” error happens in JavaScript when an async function doesn’t return a value, for example, due to an oversight on our part when writing conditional control flow.

JavaScript
async function getUserData(userId) { if (userId) { const response = await fetch(`/api/users/${userId}`); return response.json(); } // ๐Ÿ˜• No return if userId is absent } // โŒ Cannot read property 'then' of undefined if userId is absent getUserData().then(data => console.log(data));

To fix it, check all flow paths and make sure the async function always returns a value.

JavaScript
async function getUserData(userId) { if (userId) { const response = await fetch(`/api/users/${userId}`); return response.json(); } // ๐Ÿ‘ Return a resolved Promise even if userId is absent return Promise.resolve(null); } // โœ… Now, we can safely use 'then' getUserData().then(data => console.log(data));

Fix: Promise is not properly chained

The “Cannot read property ‘then’ of undefined” error occurs in JavaScript when you don’t chain the Promises properly:

JavaScript
function fetchAndParseUser(apiUrl) { fetch(apiUrl) .then((response) => { console.log(response); // ๐Ÿ˜• Forgot to return the 'json' Promise }); } // โŒ Error: Cannot read property 'then' of undefined fetchAndParseUser('/api/user') .then(data => console.log(data))

To fix it in this case, make sure that each then in the chain returns a Promise if we want to continue the chain.

JavaScript
function fetchAndParseUser(apiUrl) { // ๐Ÿ‘‡ Here, we return the 'json' Promise return fetch(apiUrl) .then((response) => { console.log(response); return response.json(); // ๐Ÿ‘ Return the Promise here }); } // โœ… Now, we can safely use 'then' fetchAndParseUser('/api/user') .then(data => console.log(data))

How to quickly detect the hover event in Vue.js

To detect mouse hover in Vue.js, use a hover state variable and the mouseenter & mouseleave events to detect when the mouse enters and leaves the element’s bounds.

App.vue
<template> <div @mouseenter="hover = true" @mouseleave="hover = false"></div> </template> <script> export default { data() { return { hover: false, }; }, }; </script>

The mouseenter event runs when the mouse pointer enters the bounds of an element, while mouseleave runs when it leaves.

We could also listen to theย mouseoverย event to detect hover, this event runs for an element and every single one of its ancestor elements in the DOM tree (i.e. it bubbles) and this could cause serious performance problems in deep hierarchies.ย mouseenterย doesnโ€™t bubble so we can use it without worrying about this.

Change style on hover in Vue.js

To change the style of an element on hover in Vue.js, we can combine the hover state variable and the :class directive:

App.vue
<template> <div @mouseenter="hover = true" @mouseleave="hover = false" class="div" :class="{ 'div-hover': hover }" ></div> </template> <script> export default { data() { return { hover: false, }; }, }; </script> <style> .div { background-color: blue; width: 200px; height: 100px; } .div-hover { background-color: yellow; } </style>
Changing color on mouse hover in Vue.js.

Display another element on hover in Vue.js

We could also display another element in the UI when we detect hover. For example: a tooltip to display more info on a particular UI element.

To do this, you can pass the state variable to a v-if state directive you set on the element. This ensures that the element only displays when the hover state is true.

App.vue
<template> <button @mouseenter="hover = true" @mouseleave="hover = false" class="div" :class="{ 'div-hover': hover }" > Button </button> <p v-if="hover">Tooltip</p> </template> <script> export default { data() { return { hover: false, }; }, }; </script>
Displaying another element on hover.

Detect hover on Vue component

You can also use the mouseenter and mouseleave approach to detect hover on a custom Vue component.

components/StyledButton.vue
<template> <div> <button>Styled button</button> </div> </template> <style> button { height: 30px; width: 100px; background-color: lightgreen; } </style>
App.vue
<template> <styled-button @mouseenter="hover = true" @mouseleave="hover = false" ></styled-button> <p v-if="hover">Tooltip</p> </template> <script> import StyledButton from './components/StyledButton.vue'; export default { components: { StyledButton, }, data() { return { hover: false, }; }, }; </script>
Detecting mouse hover on a Vue.js custom component.

If youโ€™re using Vue 2.x, youโ€™ll need to use the .native event modifier to listen for native DOM events on the Vue component:

Vue
<styled-button @mouseenter.native="hover = true" @mouseleave.native="hover = false" ></styled-button>

Key takeaways

  • Detect mouse hover in Vue.js using a hover state variable and mouseenter & mouseleave events.
  • mouseover event can cause performance issues in deep hierarchies, mouseenter is safer.
  • Use hover state to conditionally apply CSS classes, changing element appearance on hover.
  • Use hover state with v-if to conditionally display elements like tooltips on hover.
  • mouseenter and mouseleave can detect hover on custom Vue components. Use .native modifier in Vue 2.x.