49

In v5, we could add trailing ? to route for optional parameters, but as in v6, the support for the same has been dropped, so what's the alternate way of writing the following piece of code?

<Route path="/cart/:id?" component={<CartPage />} />

6 Answers 6

73

[email protected]+

Optional segments/parameters have been re-introduced to the library. The docs have been updated, but v6.5.0 Release Notes include the details as well.

The above routes can be merged to a single route:

<Route path="/cart/:id?" element={<CartPage />} />

Edit alternate-way-for-optional-parameters-in-v6

[email protected]

After quite a bit of digging through the source code to understand how path parsing was different in RRDv6 from RRDv5, and turned up nothing really other than they no longer use path-to-regex, I hit up the repo's issues section and found this issue which outright states they don't plan to support optional path parameters in v6.

See FAQ: What Happened to Regexp Routes Paths?

It seems the suggested solution is to render 2 routes to match either path and render the same component.

Example:

<Route path="/cart/:id" element={<CartPage />} />
<Route path="/cart/" element={<CartPage />} />

or

<Route path="/cart">
  <Route index element={<CartPage />} />
  <Route path=":id" element={<CartPage />} />
</Route>

The latter is really only syntactic sugar around relative paths from "/cart" though.

Sign up to request clarification or add additional context in comments.

20 Comments

Jut hit the same problem. It is quite annoying as my solution was heavily relying on the optional parameters and I have to rewrite a lot of routing now. Not to say it adds a lot of unnecessary lines.
@Vočko Not to mention that path is now also only a string, so you can't even use an array of paths to match, you have to be very explicit.
Just to contribute to this answer as it's the top voted: the support for optional parameters is back on 6.5.0: github.com/remix-run/react-router/releases/tag/…. Maybe it'd be worth it to update the answer and add it :)
Thanks, maybe put the updated solution (v6.5) on the top.
@TheDiveO The optional path params only work on the path prop of the Route component if I'm not mistaken. If you are doing some "manual" matching then I believe you need to be explicit with the paths, i.e. test for "/base/:detail" and "/base" separately.
|
7

As Drew Reese said, there is no support for the optional parameters in v6 (at least as of now).

I have ended up writing these little helper functions that register all nested routes for the optional parameters.

const registerOptionalParamRoute = (optionalParams: string[], element: Element) => {
    if (optionalParams.length === 0)
        return <Fragment/>;

    const param = optionalParams[0];
    optionalParams.splice(0, 1);

    return <Route path={param} element={element}>
        {registerOptionalParamRoute(optionalParams, element)}
    </Route>;
};

const registerOptionalParams = (path: string, element: JSX.Element) => {
    const params = path.split("/");
    let basePath = "";
    let optionalParams = [];

    for (let i = 0; i < params.length; i++) {
        if (params[i] === '')
            continue;
      
        if (!params[i].includes("?"))
            basePath += "/" + params[i];
        else
            optionalParams.push(params[i].substr(0, params[i].length - 1));
    }

    return <Route path={basePath} key={basePath} element={element}>
        {registerOptionalParamRoute(optionalParams, element)}
    </Route>;
};

Then call it:

<Routes>
    {registerOptionalParams('/component/:param1?/:param2?', <Component/>)}
</Routes>    

For an example url /component/:param1?/:param2? and given component <Component/> it generates the following jsx element:

<Route path="component" element={<Component/>}>
    <Route path=":param1" element={<Component/>}>
        <Route path=":param2" element={<Component/>} />
    </Route>
</Route>

I have also created feature request for optional parameters (https://github.com/remix-run/react-router/issues/8381), will see what feedback it will get.

1 Comment

This is awesome and works Like a charm. I do have a small suggestion. To support deep nested routes I would put / at the end of the string basePath += `${params[i]}/`;. This way you can call the function regardless of depth.
6

Use wildcard:

<Route path="/cart/*" component={<CartPage />} />

2 Comments

Does not work, parameter is not bound to variable, when using useParams.
@DirectionUnkown you can simply read the * when destructiong the result of useParams() and rename it, it would look somthing like this const { '*': myOptionalParameter} = useParams(), however this will not be helpful if you have multiple optional parameters in your route
3

It would be great if this could be described in the migration guide – or if there was at least a hint. So the recommended way of doing "optional" params really is ...

<Route path='/page/:friendlyName/:sort' element={<Page/>} />
<Route path='/page/:friendlyName/' element={<Page/>} />

Comments

0

Base react router doc

https://reactrouter.com/docs/en/v6/hooks/use-location

would not this work?

import * as React from 'react';
import { useLocation } from 'react-router-dom';

function App() {
  let location = useLocation();

  React.useEffect(() => {
    ga('send', 'pageview');
  }, [location]);

  return (
    // ...
  );
}

Comments

0
import * as React from 'react';
import {useNavigate} from "react-router-dom"

function App() {
  const location = useNavigate();

  React.useEffect(() => {
    ga('send', 'pageview');
    location("/route");

  }, [location]);

  return (
    // ...
  );
}

this is not working because it is not supported in version 6 in react router dom you must use import {useNavigate} from "react-router-dom"

1 Comment

Please put the working code in your answer. Right now it looks like you just copy-pasted Frederico's answer, which is not OK.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.