react-router-guarded-routes - npm explorer} path="/bar/*">
baz} path="/bar/baz" />





)
}
`

Of course, you can also set up separate fallbacks and guards for each route.

`tsx
import { BrowserRouter, Outlet } from 'react-router-dom'
import {
GuardConfigProvider,
GuardedRoute,
GuardedRoutes,
GuardMiddleware,
GuardProvider,
} from 'react-router-guarded-routes'

const logGuard: GuardMiddleware = (to, from, next) => {
console.log(to, from)
next()
}

const fooGuard: GuardMiddleware = (to, from, next) => {
console.log('foo')
next()
}

const guards = [logGuard]
const fooGuards = [fooGuard]

export default function App() {
return (


loading...} guards={guards}>

fallback={

loading foo...
}
guards={fooGuard}
element={
foo
}
path="/foo"
/>
element={

bar


}
path="/bar/*"
>
baz} path="/bar/baz" />





)
}
`

You can also call next.ctx('ctx value') to transfer contextual information, and get it by ctxValue in the next guard middleware. The guard middleware is executed from outside to inside, left to right.

`tsx

fallback={

loading...
}
guards={(to, from, next) => {
next.ctx('ctx value')
}}
>

guards={(to, from, next, { ctxValue }) => {
console.log(ctxValue) // ctx value
next()
}}
element={
foo
}
path="/foo"
/>



`

And call next.end() to ignore remaining middleware.

`tsx

fallback={

loading...
}
guards={
((to, from, next) => {
next.end()
},
() => {
console.log('will not be called')
})
}
>

guards={() => {
console.log('will not be called')
}}
element={
foo
}
path="/foo"
/>



`

API

$3

`ts
import React from 'react'
import {
Location,
NavigateFunction,
RouteMatch,
RouteObject,
} from 'react-router'
import { ReplacePick } from 'types-kit'

export interface GuardedRouteConfig {
guards?: GuardMiddleware[]
fallback?: React.ReactNode
[props: PropertyKey]: any
}

export type GuardedRouteObject = RouteObject &
GuardedRouteConfig & {
children?: GuardedRouteObject[]
}

export interface NextFunction extends NavigateFunction {
(): void
ctx: (value: T) => void
end: () => void
}

export interface GuardedRouteMatch
extends Omit, 'route'> {
route: GuardedRouteObject
}

export interface ToGuardRouteOptions {
location: Location
matches: GuardedRouteMatch[]
route: GuardedRouteObject
}

export interface FromGuardRouteOptions
extends ReplacePick<
ToGuardRouteOptions,
['location', 'route'],
[
ToGuardRouteOptions['location'] | null,
ToGuardRouteOptions['route'] | null
]
> {}

export interface ExternalOptions {
ctxValue: T
injectedValue: I
}

export type GuardMiddlewareFunction = (
to: ToGuardRouteOptions,
from: FromGuardRouteOptions,
next: NextFunction,
externalOptions: ExternalOptions
) => Promise | void

export type GuardMiddlewareObject = {
handler: GuardMiddlewareFunction
register?: (
to: ToGuardRouteOptions,
from: FromGuardRouteOptions
) => Promise | boolean
}
export type GuardMiddleware =
| GuardMiddlewareFunction
| GuardMiddlewareObject
`

$3

#### GuardConfigProvider

The GuardConfigProvider has configuration about routing, should not be used more than one in an app, make sure it's at the topmost level inside the Router (BrowserRouter and HashRouter).

And it provides APIs for whether to run guard middleware and whether to display the fallback element:

##### Props

`tsx
import React from 'react'

export interface GuardConfigProviderProps {
enableGuard?: (
location: ToGuardRouteOptions,
prevLocation: FromGuardRouteOptions
) => Promise | boolean
enableFallback?: (
location: ToGuardRouteOptions,
prevLocation: FromGuardRouteOptions
) => boolean
children: React.ReactNode
}
`

| Prop | Optional | Default | Description |
| ---------------- | :------: | :------------------------------------------------------------: | --------------------------------------- |
|
enableGuards | Yes | (to, from) => to.location.pathname !== from.location?.pathname | whether to run guard middleware |
|
enableFallback | Yes | () => true | whether to display the fallback element |

##### Setup

`tsx
import { BrowserRouter } from 'react-router-dom'
import { GuardConfigProvider } from 'react-router-guarded-routes'
export default function App() {
return (


{
// routes
}


)
}
`

#### GuardProvider

It provides public fallback element and guard middleware for GuardedRoute.

##### Props

`tsx
import React from 'react'

export interface GuardProviderProps {
fallback?: React.ReactElement
useInject?: (
to: ToGuardRouteOptions,
from: FromGuardRouteOptions
) => Record
guards?: GuardedRouteConfig['guards']
children: React.ReactNode
}
`

| Prop | Optional | Default | Description |
| ----------- | :------: | :-----: | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
fallback | Yes | | a fallback element to show when a GuardedRoute run guard middleware |
|
useInject | Yes | | an injected value (React hooks can be used) for guard middleware to use, will be automatically merged the values of nested GuardProvider |
|
guards | Yes | | the guards to set for routes inside the GuardProvider |

##### Setup

`tsx
import { BrowserRouter } from 'react-router-dom'
import {
GuardConfigProvider,
GuardedRoute,
GuardedRoutes,
GuardMiddleware,
GuardProvider,
} from 'react-router-guarded-routes'

const logGuard: GuardMiddleware = (to, from, next) => {
console.log(to, from)
next()
}

export default function App() {
return (


loading...} guards={[logGuard]}>

foo} path="/foo" />




)
}
`

Use nested GuardProvider:

`tsx

loading...}>

foo} path="/foo" />
loading2...}>
element={


bar


}
path="/bar/*"
>
baz} path="/bar/baz" />





`

Inject value:

`tsx
import { createContext } from 'react'
import { BrowserRouter } from 'react-router-dom'
import {
GuardConfigProvider,
GuardedRoute,
GuardedRoutes,
GuardProvider,
} from 'react-router-guarded-routes'

export const AuthContext = createContext({
isLogin: false,
})

export function useAuth() {
return useContext(AuthContext)
}

export default function App() {
return (



fallback={

loading...
}
useInject={useAuth}
guards={[
(to, from, next, { injectedValue }) => {
console.log(injectedValue) // { isLogin: false }
next()
},
]}
>

foo} path="/foo" />





)
}
`

#### GuardedRoutes

The GuardedRoutes component acts as a replacement for the default Routes component provided by React Router.

##### Props

`tsx
import { RoutesProps } from 'react-router'

export interface GuardedRoutesProps extends RoutesProps {}
`

##### Setup

`tsx



foo} path="/foo" />



`

#### GuardedRoute

The GuardedRoute component acts as a replacement for the default Route component provided by React Router, allowing for routes to use guard middleware and accepting the same props as regular Route.

##### Props

`tsx
import { Route } from 'react-router'
type RouteProps = Parameters[0]

export type GuardedRouteProps = RouteProps & GuardedRouteConfig
`

The following table explains the guard-specific props for this component.

| Prop | Optional | Default | Description |
| ---------- | :------: | :-----: | ---------------------------------------------------------------------------------------------------------------------------------- |
|
fallback | Yes | | a fallback element to show when a GuardedRoute run guard middleware. (it will override the fallback provided by GuardProvider) |
|
guards | Yes | | the guards to set for the route |

##### Setup

`tsx

element={

foo
}
path="/foo"
fallback={
loading...
}
guards={[
(to, from, next) => {
next()
},
]}
/>

`

$3

#### useGuardedRoutes

The useGuardedRoutes hook acts as a replacement for the default useRoutes hook provided by React Router, and additionally provides fallback and guards properties for each member.

##### Props

`tsx
import { useRoutes } from 'react-router'

type LocationArg = Parameters[1]

export function useGuardedRoutes(
guardedRoutes: GuardedRouteObject[],
locationArg?: LocationArg
): ReturnType
`

##### Setup

`tsx
import {
GuardedRouteObject,
useGuardedRoutes,
} from 'react-router-guarded-routes'
const routes: GuardedRouteObject[] = [
{
path: '/foo',
element:

foo
,
fallback:
loading foo...
,
guards: [(to, from, next) => next()],
},
]

function Routes() {
return <>{useGuardedRoutes(routes)}
}

export default function App() {
return (


loading...}>




)
}
``