Tari Ibaba

Tari Ibaba is a software developer with years of experience building websites and apps. He has written extensively on a wide range of programming topics and has created dozens of apps and open-source libraries.

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: 'wp.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> </> ); }

Best laptops (for programming)

Dell XPS 15 9000

View laptop

  • Screen size: 15.6”
  • Color: Black
  • Hard disk size: 512 GB
  • CPU model: Core i7
  • RAM: 16 GB
  • Resolution: 1920 x 1200
  • OS: Windows 11 Pro
  • Special features: InfinityEdge, Anti-glare screen
  • Graphics card: Arc A370M Graphics

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: 'wp.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: 'wp.codingbeautydev.com', color: 'blue', topic: 'coding', } as const; // πŸ‘ˆ const assertion // πŸ‘‡ type Values = 'wp.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: 'wp.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: 'wp.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: 'wp.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://wp.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 wp.codingbeautydev.com:

Images can now also be displayed from wp.codingbeautydev.com in this Next.js app.
Images can now also be displayed from wp.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 fix the “This project is configured to use Yarn” error on pnpm install

The “This project is configured to use Yarn” error happens in pnpm install when you have a field in your package.json file that specifies Yarn as the package manager.

The package.json field may have come about when you ran a yarn set version ... command, for example, yarn set version stable to set up Yarn Berry, or yarn set version berry to migrate from Yarn v1 to Yarn Berry.

Fix “This project is configured to use Yarn” error

To fix the this pnpm install usage error, simply remove the "packageManager" field from package.json:

Once you do that, pnpm install will run properly.

7 amazing new JavaScript features in ES14 (ES2023)

The world’s most popular programming language just got a new update.

Ever since 2015, a new JavaScript version has come out every year with tons of powerful features to make life much easier, and 2023 has been no different. Let’s look at what ES14 has to offer.

1. Array toSorted() method

Sweet syntactic sugar.

ES14 comes with a new toSorted() method that makes it easier to sort an array and return a copy without mutation.

So instead of doing this:

JavaScript
const nums = [5, 2, 6, 3, 1, 7, 4]; const clone = [...nums]; const sorted = clone.sort(); console.log(sorted); // [1, 2, 3, 4, 5, 6, 7]

We now get to do this:

JavaScript
const nums = [5, 2, 6, 3, 1, 7, 4]; const sorted = nums.toSorted(); console.log(sorted); // [1, 2, 3, 4, 5, 6, 7] console.log(nums); // [5, 2, 6, 3, 1, 7, 4]

And just like sort(), toSorted() takes a callback function that lets you decide how the sort should happen – ascending or descending, alphabetical or numeric.

JavaScript
const nums = [5, 2, 6, 3, 1, 7, 4]; const sorted = nums.toSorted((a, b) => b - a); console.log(sorted); // [7, 6, 5, 4, 3, 2, 1]

2. Hashbang grammar

What’s a hashbang?

Basically, it’s a sequence of characters in a file that tells the Linux Shell what interpreter or engine it should use to run the file. For example:

codingbeauty.sh
#!/bin/bash echo "How are you all doing today?"

With this hashbang, we can now easily run codingbeauty.sh directly in the shell, after using the chmod command to make the script executable.

Hashbangs also let us hide all the juicy implementation details in our scripts and indicate specific interpreter versions to execute the script files.

So with ES14, we can now do this effortlessly in JavaScript files, like this:

codingbeauty.js
#!/usr/bin/env node console.log("I'm doing great, how are you?");

3. Array toSpliced() method

Those immutability purists out there will no doubt be pleased with all these new Array methods.

toSorted() is to sort() as toSpliced() is to splice():

JavaScript
const colors = ['red', 'green', 'blue', 'yellow', 'pink']; const spliced = colors.toSpliced(1, 2, 'gray', 'white'); console.log(spliced); // [ 'red', 'gray', 'white', 'yellow', 'pink' ] // Original not modified console.log(colors); // ['red', 'green', 'blue', 'yellow', 'pink'];

Here toSpliced() removes 2 of the array elements, starting from index 1, and inserts 'gray' and 'white' in their place.

4. Symbols as WeakMap keys

WeakMaps; not very popular, are they?

Well, they’re a lot like Maps, except their keys can only contain non-primitive objects – no strings or numbers allowed here. These keys are stored as weak references, meaning the JavaScript engine can carry out garbage collection on the objects when it needs to if there is no other reference to the objects in memory apart from the keys.

One powerful use of WeakMaps is custom caching: by using objects as keys, you can associate cached values with specific objects. When the objects are garbage collected, the corresponding WeakMap entries are automatically removed, clearing the cache immediately.

JavaScript
const map = new Map(); const weakMap = new WeakMap(); const obj1 = { name: 'React' }; const obj2 = { name: 'Angular' }; map.set(obj1, 'Value for obj1 at Coding Beauty'); weakMap.set(obj2, 'Value for obj2 at Coding Beauty'); console.log(map.get(obj1)); // Output: Value for obj1 console.log(weakMap.get(obj2)); // Output: Value for obj2 obj1 = null; obj2 = null; console.log(map.get(obj1)); // Output: Value for obj1 console.log(weakMap.get(obj2)); // Output: undefined (obj2 has been garbage collected)

So ES14 makes it possible to define JavaScript Symbols as keys. This can make the role a key-value pair plays in a WeakMap clearer.

JavaScript
let mySymbol = Symbol('mySymbol'); let myWeakMap = new WeakMap(); let obj = { name: 'Coding Beauty' }; myWeakMap.set(mySymbol, obj); console.log(myWeakMap.get(mySymbol)); // Output: object

And what are they meant for?

5. Array toReversed() method

Another new Array method to promote immutability and functional programming.

The name’s self-explanatory: give me the reversed version of my array.

Before – with reverse().

JavaScript
const arr = [5, 4, 3, 2, 1] const reversed = arr.reverse(); console.log(reversed); // [1, 2, 3, 4, 5] // Original modified console.log(arr); // [1, 2, 3, 4, 5]

Now:

JavaScript
const arr = [5, 4, 3, 2, 1] const reversed = arr.toReversed(); console.log(reversed); // [5, 4, 3, 2, 1] // Original NOT modified console.log(arr); // [1, 2, 3, 4, 5]

6. Array find from last

Sure we can already use the Array find() method to find an element in an array that passes a specified test condition. And findIndex() will give us the index of such an element.

But find() and findIndex() both start searching from the first element of the array. What if it’ll be better for us to search from the last element instead?

Here we’re trying to get the item in the array with the value prop equal to y. With find() and findIndex():

JavaScript
const letters = [ { value: 'v' }, { value: 'w' }, { value: 'x' }, { value: 'y' }, { value: 'z' }, ]; const found = letters.find((item) => item.value === 'y'); const foundIndex = letters.findIndex((item) => item.value === 'y'); console.log(found); // { value: 'y' } console.log(foundIndex); // 3

This works, but as the target object is closer to the tail of the array, we could make this program run faster if we use the new ES2022 findLast() and findLastIndex() methods to search the array from the end.

JavaScript
const letters = [ { value: 'v' }, { value: 'w' }, { value: 'x' }, { value: 'y' }, { value: 'z' }, ]; const found = letters.findLast((item) => item.value === 'y'); const foundIndex = letters.findLastIndex((item) => item.value === 'y'); console.log(found); // { value: 'y' } console.log(foundIndex); // 3

Another use case might require that we specifically search the array from the end to get the correct item. For example, if we want to find the last even number in a list of numbers, find() and findIndex() would produce a totally wrong result.

JavaScript
const nums = [7, 14, 3, 8, 10, 9]; // gives 14, instead of 10 const lastEven = nums.find((value) => value % 2 === 0); // gives 1, instead of 4 const lastEvenIndex = nums.findIndex((value) => value % 2 === 0); console.log(lastEven); // 14 console.log(lastEvenIndex); // 1

Yes, we could call the reverse() method on the array to reverse the order of the elements before calling find() and findIndex().

But this approach would cause unnecessary mutation of the array, as reverse() reverses the elements of an array in place. The only way to avoid this mutation would be to make a new copy of the entire array, which could cause performance problems for large arrays.

And this beside the fact that findIndex() would still not work on the reversed array, as reversing the elements would also mean changing the indexes they had in the original array. To get the original index, we would need to perform an additional calculation, which means writing more code.

JavaScript
const nums = [7, 14, 3, 8, 10, 9]; // Copying the entire array with the spread syntax before // calling reverse() const reversed = [...nums].reverse(); // correctly gives 10 const lastEven = reversed.find((value) => value % 2 === 0); // gives 1, instead of 4 const reversedIndex = reversed.findIndex((value) => value % 2 === 0); // Need to re-calculate to get original index const lastEvenIndex = reversed.length - 1 - reversedIndex; console.log(lastEven); // 10 console.log(reversedIndex); // 1 console.log(lastEvenIndex); // 4

It’s in cases like where the findLast() and findLastIndex() methods come in handy.

JavaScript
const nums = [7, 14, 3, 8, 10, 9]; const lastEven = nums.findLast((num) => num % 2 === 0); const lastEvenIndex = nums.findLastIndex((num) => num % 2 === 0); console.log(lastEven); // 10 console.log(lastEvenIndex); // 4

This code is shorter and more readable. Most importantly, it produces the correct result.

7. Array with() method

Unlike the others, with() has no complementary mutating method. But once you see it in action, you’ll know that it’s the immutable approach to changing a single element.

In so many languages, you typically modify a single array element like this:

JavaScript
const arr = [5, 4, 7, 2, 1] // Mutates array to change element arr[2] = 3; console.log(arr); // [5, 4, 3, 2, 1]

But see what we can do now, in ES2023 JavaScript:

JavaScript
const arr = [5, 4, 7, 2, 1]; const replaced = arr.with(2, 3); console.log(replaced); // [5, 4, 3, 2, 1] // Original not modified console.log(arr); // [5, 4, 7, 2, 1]

Final thoughts

This year was all about easier functional programming and immutability.

Indeed, the reliability and consistency that immutability brings cannot be overstated. With the rise of declarative frameworks like React and Vue as well as Redux and other libraries, we’ve seen immutable JavaScript array methods explode in popularity; it’s only natural that we see more and more of them come baked into the language as it matures.

They stole the show in 2023, just like JavaScript took over a huge chunk of the language ecosystem a while back, with many millions of developers keeping that fire burning today. Let’s see what the future holds.

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))