React-router: Add a general NoMatch route

Created on 7 May 2017  路  5Comments  路  Source: ReactTraining/react-router

This is a feature request to handle _NoMatch_ route in a more global generic way.

I have a codepen that is based on React Router Template that shows how a _NoMatch_ component is not rendered even if you go to a URL that has no match in the router configuration.

When I move the _NoMatch_ Route into AboutTopicsRoute Switch it all works fine. However, I don't want it there since my application might grow and there are different routes in different files and it gets confusing to move the _NoMatch_ around just so it renders correctly.

There are some solutions, like mentioned in a comment on issue#4685, out there but most of them rely on some sort of state being introduced and that seems to make things overly complicated.

I also find it strange that we all have to add some boilerplate code just to handle _NoMatch_ since it is a fairly basic case all applications using routing need to handle.

It would be simple to add a prop like <Route nomatch component={NoMatch} /> and if you need more control depending on the route that wasn't found you could simply use the render variation of Route like so

<Route 
  nomatch 
  render={
    ({ location }) => {
      if(location.pathname.startsWith('/user'))
        return <div>missing user nested</div>
      return <div>something missing</div>
    }
  }
/>

This gives you complete freedom to handle no match in any way you see fit without any specific state being introduced.

Most helpful comment

Just use Route with a path of * at the end of your Switch. It will always match, but occurs last and will only be used if nothing else matches.

All 5 comments

Just use Route with a path of * at the end of your Switch. It will always match, but occurs last and will only be used if nothing else matches.

Thank you, @timdorr, for taking the time to look at the issue. However, your solution doesn't work.

Using the following route configuration will never render the _NoMatch_ component as you can see in my codepen

<Switch>
  <Route exact path="/" component={Home}/>
  <Switch>
    <Route path="/about" component={About}/>
    <Route path="/topics" component={Topics}/>
  </Switch> 
  <Route path="*" component={()=>(<div>NoMatch</div>)} />
</Switch>

If I move <Route path="*" component={()=>(<div>NoMatch</div>)} /> into my nested Switch it will render and work as expected.

<Switch>
  <Route exact path="/" component={Home}/>
  <Switch>
    <Route path="/about" component={About}/>
    <Route path="/topics" component={Topics}/>
    <Route path="*" component={()=>(<div>NoMatch</div>)} />
  </Switch> 
</Switch>

But if I add a route below the nested _Switch_ it will never be hit since * will catch all that is not above.

<Switch>
  <Route exact path="/" component={Home}/>
  <Switch>
    <Route path="/about" component={About}/>
    <Route path="/topics" component={Topics}/>
    <Route path="*" component={()=>(<div>NoMatch</div>)} />
  </Switch> 
  <Route path="/never" component={()=>(<div>Never hit :(</div>)} />
</Switch>

How do you suggest I move on? Simply move the * to the bottom of it all? Well, I tried that and ended up with this.

<Switch>
  <Route exact path="/" component={Home}/>
  <Switch>
    <Route path="/about" component={About}/>
    <Route path="/topics" component={Topics}/>
  </Switch> 
  <Route path="/never" component={()=>(<div>Never hit :(</div>)} />
  <Route path="*" component={()=>(<div>NoMatch</div>)} />
</Switch>

And now _Never hit :(_ and _NoMatch_ never renders...

Am I simply not getting it?

Nested <Switch>es are not supported. See docs:

All children of a <Switch> should be <Route> or <Redirect> elements.

Try replacing the <Switch> in your AboutTopicsRoute with an array.

Thank you so much @taurose that was exactly what I needed. Very helpful comment.

I guess I simply didn't get it ;)

Isn't it the case that statements could be nested if they occur inside components. I.e. one renders a component which itself has a ?

I think something like this could be used instead of to just always have a noMatch route:

import React from 'react';
import { Switch, Route } from 'react-router-dom';
import _404Component from '../Page/_404Component';

const SwitchWith404 = props => (
  <Switch>
    {props.children}
    <Route component={_404Component} />
  </Switch>
);
export default SwitchWith404;

And then instead of using you use

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ackvf picture ackvf  路  3Comments

sarbbottam picture sarbbottam  路  3Comments

davetgreen picture davetgreen  路  3Comments

andrewpillar picture andrewpillar  路  3Comments

imWildCat picture imWildCat  路  3Comments