Creating URL-Friendly Slugs in JavaScript

2023-03-06
By: O. Wolfson

Introduction

The function described in this article takes a string as input and generates a URL-friendly slug from it. The function also checks if there are any existing posts with the same slug and appends a number to the end of the slug to make it unique if necessary.

Example, we can change 'My Title' to 'my-title'.

Steps

  1. First, create a regular expression that matches any characters that are not alphanumeric or whitespace. This regular expression will be used to remove any special characters from the input string. We will also create a regular expression that matches one or more whitespace characters, which will be used to replace spaces with hyphens.
js
const specialCharsRegex = /[^\w\s]/gi;
const whitespaceRegex = /\s+/g;

If you want to know more about how this regex works, please click here.

  1. Next, define the generateSlug function with an async keyword before the function keyword. This will allow us to use the await keyword inside the function.
js
async function generateSlug(title) {
  // function body
}
  1. Inside the function, convert the input string to lowercase and remove any special characters using the replace() method and the specialCharsRegex regular expression.
js
const slug = title.toLowerCase().replace(specialCharsRegex, "");
  1. Replace any whitespace characters in the slug with hyphens using the replace() method and the whitespaceRegex regular expression.
js
const formattedSlug = slug.replace(whitespaceRegex, "-");
  1. Call the function that checks if there are any existing posts with the same slug. This function should take the slug as input and return a Promise that resolves to an array of existing posts. We will call this function checkForExistingPosts for this example.
js
const existingPosts = await checkForExistingPosts(formattedSlug);
  1. Check if the existingPosts array has any items. If it does, append the length of the array to the slug and return the formatted slug. Otherwise, return the original slug.
js
if (existingPosts.length > 0) {
  const formattedSlugWithCount = `${formattedSlug}-${existingPosts.length}`;
  return formattedSlugWithCount;
} else {
  return formattedSlug;
}
  1. Finally, return the formatted slug from the function.
js
return formattedSlug;

Full Code Example

Here's the full code example with comments explaining each step:

js
// Define regular expressions for special characters and whitespace
const specialCharsRegex = /[^\w\s]/gi;
const whitespaceRegex = /\s+/g;

// Define the async function that generates a slug from a title
async function generateSlug(title) {
  // Convert the title to lowercase and remove special characters
  const slug = title.toLowerCase().replace(specialCharsRegex, "");

  // Replace whitespace with hyphens
  const formattedSlug = slug.replace(whitespaceRegex, "-");

  // Check for existing posts with the same slug
  const existingPosts = await checkForExistingPosts(formattedSlug);

  // If there are existing posts, append the number of existing posts to the slug
  if (existingPosts.length > 0) {
    const formattedSlugWithCount = `${formattedSlug}-${existingPosts.length}`;
    return formattedSlugWithCount;
  } else {
    return formattedSlug;
  }
}

// Define the function that checks for existing posts with the same title
async function checkForExistingPosts(slug) {
  // Make an API call to check for existing posts with the same slug.
  // Example API call:
  // const existingPosts = await fetch(`https://example.com/api/posts?slug=${slug}`);
  return new Promise((resolve, reject) => {
    // In this example, we'll just return an empty array since we don't have an API to call
    const existingPosts = [];
    resolve(existingPosts);
  });
}

// Usage example
const title = "How to Generate a Unique URL-Friendly Slug in JavaScript";
generateSlug(title)
  .then((formattedSlug) => {
    console.log(`The formatted slug for "${title}" is "${formattedSlug}".`);
  })
  .catch((error) => {
    console.error("An error occurred while generating the slug:", error);
  });

In this example, we define the generateSlug function and the checkForExistingPosts function. The generateSlug function takes a title parameter as input, generates a slug from the title, and checks for existing posts with the same title. If there are any existing posts, the function appends a number to the end of the slug to make it unique.

We also define a checkForExistingPosts function that returns a Promise that resolves to an empty array, since we don't have an API to call in this example.

Finally, we call the generateSlug function with a title parameter, and log the formatted slug to the console. If an error occurs while generating the slug, we log an error message to the console.

The formatted slug for "How to Generate a Unique URL-Friendly Slug in JavaScript", assuming that there are no existing posts with the same title, the output of the example code I provided would look something like this:

bash
"how-to-generate-a-unique-url-friendly-slug-in-javascript".

The formatted slug for "How to Generate a Unique URL-Friendly Slug in JavaScript", if there were existing posts with the same title, the output might look something like this:

bash
"how-to-generate-a-unique-url-friendly-slug-in-javascript-3".

In this case, the number 3 is appended to the end of the slug to make it unique.

Notes On the Regex in this tutorial

The first regular expression specialCharsRegex /[^\w\s]/gi matches any character that is not a word character (\w) or whitespace (\s). The ^ character inside the square brackets means "not", so [^\w\s] matches any character that is not a word character or whitespace. The g flag at the end of the regex indicates that the search should be global, meaning it should match all occurrences of the pattern in the input string. The i flag indicates that the search should be case-insensitive, so it will match both uppercase and lowercase letters. For example, given the input string "Hello, world! 123", specialCharsRegex would match the , and ! characters. We use this expression to remove any special (non-url-friendly) characters from the input string.

The second regular expression whitespaceRegex /\s+/g matches one or more consecutive whitespace characters. The \s meta-character matches any whitespace character, such as spaces, tabs, and line breaks. The + character after \s means "one or more occurrences of the previous character or group", so \s+ matches one or more consecutive whitespace characters. The g flag again indicates that the search should be global, meaning it should match all occurrences of the pattern in the input string. We use this expression to replace any whitespace characters with hyphens.