Using optional parameters in the URL with the Next.js App Router

I am currently tackling a project that needs to accommodate different types of URL parameters. In my Next.js setup, I am relying on the App Router, which means only “next/navigation” is active for me, excluding “next/router”.

My goal is to structure my URL in this manner:

/Surveys/(shortname)/id/Report

Here, both ‘shortname’ and ‘id’ are variables. However, ‘id’ is a mandatory part of the URL, whereas ‘shortname’ is optional. For example:

/Surveys/helloworld/123/Report

and

/Surveys/123/Report

Both of these URLs should lead to the same page, where I display:

<h1>Survey shortname: helloworld</h1>
<p>ID: 123</p>

Alternatively, if there is no ‘shortname’:

<h1>Survey</h1>
<p>ID: 123</p>

Initially, I attempted to utilize the Optional Catch-all Segments feature in Next.js. However, this proved to be challenging as [[…shortname]] must be the final part of the URL, whereas I need ‘Report’ at the end.

Subsequently, I explored the idea of using slugs like [shortname] and [id] as directories. Unfortunately, this approach failed as I couldn’t proceed without ‘shortname’.

Next, I experimented with rewrite rules in my next.config.mjs file. For instance:

/** @type {import('next').NextConfig} */
const nextConfig = {
    async rewrites() {
        return [
            {
                source: '/Surveys/:shortname/:id/Reports',
                destination: '/Surveys/Reports/[...slugs]',
            },
        ];
    },
};

export default nextConfig;

The concept was to deceive Next.js into thinking the catch-all is positioned at the end. Unfortunately, this approach also did not yield the desired results.

I would greatly appreciate any guidance or examples you can offer. If you could provide a sample project with an enhanced folder structure using tsx, I would be extremely thankful!

Answer №1

There may be multiple approaches to tackling this issue, but here's one suggestion:

Utilizing catchall segments should be feasible, however, the specified pattern mandates that [[…catchall]] must encompass everything after /Surveys/.

It's crucial to differentiate within the page between routes that contain or lack a shortname path element, include or exclude Report, etc.

The details about whether shortname aligns with a predefined set of possible strings, or if id is always numerical, could impact how the values are parsed.

The folder structure should be as follows:

// "rest" will gather all segments following `/Surveys/` into an array of varying length.
app
- Surveys
--[[...rest]]
---page.tsx

As outlined in the documentation, the parameters within page.tsx would look something like this:

For a sample route: /Surveys/helloworld/123/Report

params = {rest: ['helloworld', '123', 'Report']}

Hence, you'll require a logical approach to determine which elements of rest – [0, 1, 2] – should be allocated to which variables. If the presence of shortname is genuinely optional (with no stated reason for its inclusion or exclusion, and the desired page remains the same without it), focusing on the second-to-last array element (the ID) seems essential.

const id = parseInt(rest[rest.length - 2], 10);  // extract ID and convert to an integer

// If the presence or absence of "Report" in the path is also significant...
const isReport = rest[rest.length - 1] === "Report";

Subsequently, proceed with your page rendering logic.

From my perspective, structuring your routes to increase specificity with each additional path element, positioning the ID as the final element, and sidestepping optional elements devoid of semantic value (looking at you, shortname!) could prove more efficient.

If there are any additional stipulations, don't forget to update your query.

Answer №2

Hello to all who come across this message,

I was able to resolve the issue by using a simple [id] slug. In my directory structure, it looked like this:

/Surveys/[id]/Report

Instead of just having the id, users can also input additional information like:

/Survey/34234-helloworld/Report

I took the entire slug and split it by the "-", extracting the necessary parts.

export default function Reports({ params }: { params: { id: string } }) {
  const idParts = params.id.split('-');
  const id = idParts[0];
  const shortname = idParts.length > 1 ? idParts[1] : null;

  return (
    <h1>
      Reports with ID: {id} 
      {shortname && ` and shortname: ${shortname}`}
    </h1>
  );
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How can you enable fullscreen for a featherlight iFrame?

I have implemented Featherlight to display an iframe within a popup modal on my website. If you click the iframe button, you can see a demo of how it works. One issue I am facing is that the generated iframe tag by Featherlight does not include allowfulls ...

Capturing numerous data points with JavaScript

<span> <label class="label">Color</label> <span class="foo"><input name="Color" value="Blue" class="customs" maxlength="100" type="text"/></span> </span> </span> <span> <label cla ...

jQuery: Implementing smooth horizontal scrolling functionality

Here is the code I am working with: // General Function $("li a").click(function() { $("li a").removeClass("active"); $(this).addClass("active"); }); // Horizontal Scroll-to Function $("li a").click(function() { var offset = this.getBounding ...

Implementing event handlers with 'v-on' on dynamically inserted content using 'v-html' in Vue

Can Vue allow for the addition of v-on events on strings included in v-html? In this scenario, clicking the 'Maggie' link doesn't produce any action. It appears that it's not recognized by Vue. Is there an alternative method to achieve ...

How can you calculate the number of elements in a jQuery array and iterate through each of them?

After extracting a string from a mySQL query with PHP, my AJAX script comes into play. This string is then dissected and placed into a jQuery array. The results are displayed on the screen using .html() The length of this array can range from zero items t ...

What is the source of this error message "Exceeding maximum characters in string literal"?

Hey everyone! Sorry for the bother, but I'm a bit stumped on this issue. Within my view, I have the following code snippet: <fieldset> <dl> <dt> <label for="FormTypes">Form Type:</label> ...

Shading THREE.js Color with Textures

I have added a simple box to my scene, and I am looking to create a shader that will apply a texture to it and add color to this texture. Here is my vertex shader (nothing special about it): <script id="vertexShader" type="x-shader/x-vertex"> ...

Exploring Angular 2 Beta 8: An Introduction to @Query Usage

My attempt to utilize @Query to fetch data regarding an element in my template has not been successful. I made an effort using the following approach: Referenced here. Here is a snippet of my code, import {Component, Query, QueryList, ElementRef} from &a ...

Conceal the div element without revealing it beforehand

Is there a method to conceal a div without it initially loading? When I attempt to hide the div, it briefly appears for about 0.5 seconds before disappearing, which makes the animation look unattractive. Is there a way to prevent this, or am I approaching ...

What seems to be the issue with this Discord.js kick command code? It's not

Okay, so I'm in the process of creating a kick command for my Discord bot. The issue I'm encountering is that when no reason is specified or if a user is not mentioned to be kicked, the bot responds correctly but does not actually kick the user. ...

The Vuetify v-btn within the v-bottom-navigation remains in an active state even when it has not

I am encountering an issue with a button in the bottom navigation bar of my Vuetify application. I have set up several buttons as routes in Nuxtjs, which become active when the corresponding page is visited. However, there is a button in the bottom nav bar ...

What is the best way to conceal all input elements in a form?

I have been attempting to conceal input elements within my form by using the code snippet below. Unfortunately, it doesn't seem to be functioning as expected... <html lang="en"> <head> <title>example</title> < ...

Implementing a document update event using jQuery

On my WordPress site, I am using a responsive lightbox plugin. When an image is clicked, it opens a popup box with the ID fullResImage. Now, I would like to incorporate the Pinch Zoomer function to it. Do I need to bind a function for this, or is there a s ...

typescript array filter attributes

I encountered a situation where I had 2 separate arrays: items = [ { offenceType:"7", offenceCode:"JLN14", }, { offenceType:"48", offenceCode:"JLN14", } ]; demo = [ { offenceCode: 'JLN14&apo ...

Guide on detecting errors when parameters are not provided with req.params in Node.js

My question revolves around a piece of code that I have been working on. Here is the snippet: const express = require('express') const app = express() app.get('/test/:name', (req, res) => { const {name} = req.params; res.send(`P ...

Quicker Solution to Iteration in Google Apps Script with JavaScript

I've set up a for loop to extract sales data from an array (salesLog) and transfer it to a designated sheet (targetSheet) in columns. The sales data is spread across multiple columns in the array. The loop adds up the columns between columnStart and c ...

An easy way to switch animations using CSS display:none

Dealing with some missing gaps here, hoping to connect the dots and figure this out. I'm attempting to create a functionality where a div slides in and out of view each time a button is clicked. Eventually, I want multiple divs to slide out simultane ...

Ways to enhance a Vue component using slots

I am looking to enhance a third-party library component by adding an extra element and using it in the same way as before. For example: <third-party foo="bar" john="doe" propsFromOriginalLibrary="prop"> <template v ...

The functionality of react-waypoint's onEnter/onLeave event handlers seems to be malfunctioning

Recently, I experimented with react-waypoint to control the visibility of a div. The code works as intended by hiding the div when it reaches the waypoint inside onEnter. When the div is inside, the isInView state becomes true, which in turn triggers the d ...

Manipulating Object origin data

I'm unsure if this can be achieved. I am aiming to create a header and footer that remain the same while only switching the content pages. The resources I have at my disposal cover HTML, JS, CSS, and JQuery. Below is a simplified version of my curre ...