CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-reach--router

Next generation React routing library with accessible navigation and focus management.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

advanced-routing.mddocs/

Advanced Routing

Advanced routing patterns including redirects, conditional rendering, and route matching utilities.

Capabilities

Redirect Component

Declarative redirect component for programmatic navigation during rendering.

/**
 * Declarative redirect component for programmatic navigation
 * @param props - Redirect configuration
 */
function Redirect(props: {
  from?: string;
  to: string;
  replace?: boolean;
  state?: any;
  noThrow?: boolean;
}): React.ReactElement;

Props:

  • from (string, optional) - Source path pattern (only used inside Router)
  • to (string, required) - Destination path
  • replace (boolean, optional) - Replace current history entry (defaults to true)
  • state (any, optional) - State to pass with redirect
  • noThrow (boolean, optional) - Don't throw redirect error (for server rendering)

Usage Examples:

import React from "react";
import { Router, Redirect } from "@reach/router";

// Basic redirect
const App = () => (
  <Router>
    <Home path="/" />
    <Redirect from="/home" to="/" />
    <About path="/about" />
  </Router>
);

// Redirect with parameters
const App = () => (
  <Router>
    <UserProfile path="/users/:userId" />
    <Redirect from="/profile/:userId" to="/users/:userId" />
  </Router>
);

// Conditional redirect based on authentication
const ProtectedRoute = ({ component: Component, ...props }) => {
  const isAuthenticated = useAuth();
  
  if (!isAuthenticated) {
    return <Redirect to="/login" state={{ from: props.path }} />;
  }
  
  return <Component {...props} />;
};

<Router>
  <Login path="/login" />
  <ProtectedRoute component={Dashboard} path="/dashboard" />
</Router>

// Redirect with state
const LoginRedirect = () => (
  <Redirect 
    to="/dashboard" 
    state={{ message: "Successfully logged in!" }}
  />
);

// Server-safe redirect
const ServerSafeRedirect = ({ to }) => (
  <Redirect to={to} noThrow />
);

// Multiple redirects for old URLs
const LegacyRedirects = () => (
  <Router>
    <Home path="/" />
    <Redirect from="/old-home" to="/" />
    <Redirect from="/legacy/about" to="/about" />
    <Redirect from="/v1/users/:id" to="/users/:id" />
    <About path="/about" />
    <User path="/users/:id" />
  </Router>
);

Match Component

Render prop component for conditional rendering based on path matching without routing.

/**
 * Render prop component for conditional rendering based on path matching
 * @param props - Match configuration
 */
function Match(props: {
  path: string;
  children: (props: MatchRenderProps) => React.ReactNode;
}): React.ReactElement;

interface MatchRenderProps {
  navigate: (to: string | number, options?: NavigateOptions) => Promise<void>;
  location: Location;
  match: MatchResult | null;
}

interface MatchResult {
  uri: string; // Always normalized with leading slash
  path: string;
  [paramName: string]: string | string[]; // Can contain arrays for splat routes
}

Usage Examples:

import React from "react";
import { Match } from "@reach/router";

// Conditional navigation based on match
const ConditionalNav = () => (
  <Match path="/admin/*">
    {({ match }) => (
      <nav>
        <Link to="/">Home</Link>
        {match && (
          <>
            <Link to="/admin/users">Users</Link>
            <Link to="/admin/settings">Settings</Link>
          </>
        )}
      </nav>
    )}
  </Match>
);

// Dynamic styling based on path match
const Header = () => (
  <Match path="/products/*">
    {({ match, location }) => (
      <header className={match ? "products-header" : "default-header"}>
        <h1>My Store</h1>
        {match && <p>Browsing: {location.pathname}</p>}
      </header>
    )}
  </Match>
);

// Multiple matches for complex layouts
const Layout = ({ children }) => (
  <div>
    <Match path="/dashboard/*">
      {({ match }) => match && <DashboardSidebar />}
    </Match>
    
    <Match path="/admin/*">
      {({ match }) => match && <AdminToolbar />}
    </Match>
    
    <main>{children}</main>
  </div>
);

// Match with parameter extraction
const BreadcrumbTrail = () => (
  <Match path="/users/:userId/posts/:postId">
    {({ match, navigate }) => {
      if (!match) return null;
      
      const { userId, postId } = match;
      
      return (
        <nav className="breadcrumb">
          <button onClick={() => navigate("/")}>Home</button>
          <span> / </span>
          <button onClick={() => navigate("/users")}>Users</button>
          <span> / </span>
          <button onClick={() => navigate(`/users/${userId}`)}>
            User {userId}
          </button>
          <span> / </span>
          <span>Post {postId}</span>
        </nav>
      );
    }}
  </Match>
);

// Conditional component mounting
const FeatureToggle = ({ feature, children }) => (
  <Match path={`/features/${feature}/*`}>
    {({ match }) => match ? children : null}
  </Match>
);

<FeatureToggle feature="beta">
  <BetaFeatures />
</FeatureToggle>

matchPath Utility

Programmatic route matching utility for checking if a path matches a pattern outside of components.

/**
 * Programmatic route matching utility
 * @param path - Path pattern to match against
 * @param uri - URI to test for matching
 * @returns Match result with params and uri, or null if no match
 */
function matchPath(path: string, uri: string): MatchResult | null;

interface MatchResult {
  route: {
    path: string;
  };
  params: Record<string, string | string[]>; // Can contain arrays for splat routes
  uri: string; // Always normalized with leading slash
}

Usage Examples:

import { matchPath } from "@reach/router";

// Basic path matching
const match = matchPath("/users/:userId", "/users/123");
if (match) {
  console.log(match.params.userId); // "123"
  console.log(match.uri); // "/users/123"
}

// Wildcard matching
const wildcardMatch = matchPath("/files/*", "/files/docs/readme.txt");
if (wildcardMatch) {
  console.log(wildcardMatch.params["*"]); // "docs/readme.txt"
}

// Route-based data fetching
const fetchRouteData = async (pathname) => {
  const userMatch = matchPath("/users/:userId", pathname);
  if (userMatch) {
    return await fetchUser(userMatch.params.userId);
  }
  
  const postMatch = matchPath("/posts/:postId", pathname);
  if (postMatch) {
    return await fetchPost(postMatch.params.postId);
  }
  
  return null;
};

// Server-side route matching
const getServerSideProps = async (context) => {
  const { req } = context;
  const pathname = req.url;
  
  const match = matchPath("/products/:productId", pathname);
  if (match) {
    const product = await fetchProduct(match.params.productId);
    return { props: { product } };
  }
  
  return { props: {} };
};

// Route guards and middleware
const routeGuards = [
  {
    pattern: "/admin/*",
    guard: (match, user) => user?.isAdmin
  },
  {
    pattern: "/users/:userId/*",
    guard: (match, user) => user?.id === match.params.userId
  }
];

const checkAccess = (pathname, user) => {
  for (const { pattern, guard } of routeGuards) {
    const match = matchPath(pattern, pathname);
    if (match && !guard(match, user)) {
      return false;
    }
  }
  return true;
};

// Dynamic route configuration
const routes = [
  { pattern: "/", component: "Home" },
  { pattern: "/about", component: "About" },
  { pattern: "/users/:userId", component: "UserProfile" },
  { pattern: "/posts/:postId", component: "PostDetail" }
];

const findMatchingRoute = (pathname) => {
  for (const route of routes) {
    const match = matchPath(route.pattern, pathname);
    if (match) {
      return { ...route, match };
    }
  }
  return null;
};

Redirect Utilities

Utility functions for handling redirects programmatically.

/**
 * Check if an error is a redirect request
 * @param error - Error object to check
 * @returns True if error is a redirect request
 */
function isRedirect(error: any): boolean;

/**
 * Throw a redirect request for error boundary handling
 * @param to - Destination path
 * @throws RedirectRequest that can be caught by error boundaries
 */
function redirectTo(to: string): never;

Usage Examples:

import { isRedirect, redirectTo } from "@reach/router";

// Error boundary handling redirects
class RedirectBoundary extends React.Component {
  componentDidCatch(error, errorInfo) {
    if (isRedirect(error)) {
      // Handle redirect in error boundary
      console.log("Caught redirect to:", error.uri);
      // The redirect will be handled automatically
    } else {
      // Handle other errors
      console.error("Component error:", error);
    }
  }
  
  render() {
    return this.props.children;
  }
}

// Programmatic redirects in components
const AuthCheck = ({ children }) => {
  const user = useAuth();
  
  React.useEffect(() => {
    if (!user) {
      // Throw redirect that will be caught by LocationProvider
      redirectTo("/login");
    }
  }, [user]);
  
  return user ? children : null;
};

// Conditional redirects in render
const ConditionalRedirect = ({ condition, to, children }) => {
  if (condition) {
    redirectTo(to);
  }
  
  return children;
};

// Redirect with data validation
const ValidatedRoute = ({ data, fallback, children }) => {
  React.useEffect(() => {
    if (!data || !data.isValid) {
      redirectTo(fallback);
    }
  }, [data, fallback]);
  
  return data?.isValid ? children : null;
};

// Error handling with redirect detection
const handleAsyncAction = async () => {
  try {
    await performAction();
  } catch (error) {
    if (isRedirect(error)) {
      // Don't log redirect errors
      return;
    }
    
    // Log and handle other errors
    console.error("Action failed:", error);
    redirectTo("/error");
  }
};

// Custom redirect hook
const useRedirectIf = (condition, to) => {
  React.useEffect(() => {
    if (condition) {
      redirectTo(to);
    }
  }, [condition, to]);
};

// Usage of custom hook
const ProtectedComponent = () => {
  const user = useAuth();
  useRedirectIf(!user, "/login");
  
  return <div>Protected content</div>;
};

Advanced Route Patterns

Complex routing patterns and techniques for sophisticated applications.

Usage Examples:

// Route-based code splitting
const LazyRoute = ({ component, ...props }) => {
  const [Component, setComponent] = React.useState(null);
  
  React.useEffect(() => {
    import(`../components/${component}`)
      .then(module => setComponent(() => module.default));
  }, [component]);
  
  if (!Component) {
    return <div>Loading...</div>;
  }
  
  return <Component {...props} />;
};

<Router>
  <LazyRoute component="Home" path="/" />
  <LazyRoute component="About" path="/about" />
</Router>

// Nested routing with context
const NestedRoutes = () => (
  <Router>
    <Dashboard path="/dashboard/*">
      <DashboardHome path="/" />
      <Settings path="/settings/*">
        <GeneralSettings path="/" />
        <SecuritySettings path="/security" />
        <ProfileSettings path="/profile" />
      </Settings>
    </Dashboard>
  </Router>
);

// Route-based theming
const ThemedRouter = () => {
  const location = useLocation();
  const theme = getThemeForPath(location.pathname);
  
  return (
    <ThemeProvider theme={theme}>
      <Router>
        <Home path="/" />
        <Admin path="/admin/*" />
        <Public path="/public/*" />
      </Router>
    </ThemeProvider>
  );
};

// Dynamic route generation
const DynamicRoutes = ({ routes }) => (
  <Router>
    {routes.map(route => {
      const Component = route.component;
      return (
        <Component 
          key={route.path}
          path={route.path}
          {...route.props}
        />
      );
    })}
  </Router>
);

// Route transition animations
const AnimatedRouter = ({ children }) => {
  const location = useLocation();
  
  return (
    <TransitionGroup>
      <CSSTransition
        key={location.pathname}
        classNames="route"
        timeout={300}
      >
        <Router location={location}>
          {children}
        </Router>
      </CSSTransition>
    </TransitionGroup>
  );
};

Install with Tessl CLI

npx tessl i tessl/npm-reach--router

docs

advanced-routing.md

history.md

hooks.md

index.md

location-context.md

navigation.md

routing.md

server-rendering.md

utilities.md

tile.json