Frontend Development 19 min read

In‑Depth Source Code Analysis of React‑Router (v4/v5) and Its Core Components

This article provides a comprehensive walkthrough of React‑Router's internal architecture, detailing the implementation of key components such as Router, Route, Switch, Redirect, Prompt, Link, and withRouter, while explaining how history, matchPath, and path‑to‑regexp work together to enable client‑side routing in modern React applications.

Qunar Tech Salon
Qunar Tech Salon
Qunar Tech Salon
In‑Depth Source Code Analysis of React‑Router (v4/v5) and Its Core Components

The author introduces their background and the motivation for studying react‑router, noting the need to understand its inner workings to improve development efficiency.

React 16.3 introduced official Context support, and react‑router began adopting Context APIs from early 2018. The article uses version 4.3.1 of react‑router for illustration.

React‑router v4 is a pure React implementation of routing, built on top of the history library. It provides three types of history implementations—browser, hash, and memory—each exposing methods like push, replace, go, goBack, and goForward.

The project structure of react‑router and react‑router‑dom is compared, showing that many components are re‑exported from the core library.

Router Component

The Router component is the top‑level provider of routing context. It receives a required history prop and sets up a context object containing the current router state. It listens to history changes in componentWillMount (for server‑side rendering compatibility) and updates its internal match state via computeMatch() . The component only renders a single child and uses getChildContext() to pass the router data to descendants.

getChildContext() {
  return {
    router: {
      ...this.context.router,
      history: this.props.history,
      route: {
        location: this.props.history.location,
        match: this.state.match
      }
    }
  };
}

Route Component

The Route component matches the current location against a path using matchPath() . It supports three rendering props— component , render , and children —which are evaluated in that order. When a match occurs, it passes { match, location, history, staticContext } to the rendered element.

render() {
  const { match } = this.state;
  const { children, component, render } = this.props;
  const { history, route, staticContext } = this.context.router;
  const location = this.props.location || route.location;
  const props = { match, location, history, staticContext };
  if (component) return match ? React.createElement(component, props) : null;
  if (render) return match ? render(props) : null;
  if (typeof children === "function") return children(props);
  if (children && !isEmptyChildren(children)) return React.Children.only(children);
  return null;
}

matchPath Function

matchPath() converts a path pattern into a regular expression using path‑to‑regexp , then tests the current pathname. It returns an object containing path , url , isExact , and extracted params if the match succeeds.

function matchPath(pathname, options = {}, parent) {
  const { path, exact = false, strict = false, sensitive = false } = options;
  if (!path) return parent;
  const { re, keys } = compilePath(path, { end: exact, strict, sensitive });
  const match = re.exec(pathname);
  if (!match) return null;
  const [url, ...values] = match;
  const isExact = pathname === url;
  if (exact && !isExact) return null;
  const params = keys.reduce((memo, key, index) => {
    memo[key.name] = values[index];
    return memo;
  }, {});
  return { path, url: path === '/' && url === '' ? '/' : url, isExact, params };
}

Switch Component

Switch iterates over its children and renders the first Route that matches the current location, using the same matchPath() logic.

render() {
  const { route } = this.context.router;
  const { children } = this.props;
  const location = this.props.location || route.location;
  let match;
  React.Children.forEach(children, (element) => {
    if (!match && React.isValidElement(element)) {
      const { path, exact, strict, sensitive } = element.props;
      const child = element;
      match = matchPath(location.pathname, { path, exact, strict, sensitive }, route.match);
      if (match) return React.cloneElement(child, { location, computedMatch: match });
    }
  });
  return match;
}

Redirect Component

The Redirect component performs a navigation by calling history.push or history.replace . It computes the target URL with generatePath() when route parameters are needed, and handles server‑side rendering via isStatic() .

perform() {
  const { computedMatch, to } = this.props;
  const location = computeTo({ computedMatch, to });
  this.context.router.history[replace ? 'replace' : 'push'](location);
}

Prompt Component

Prompt uses history.block() to intercept navigation when the when prop is true, displaying a custom message or function.

enable(message) {
  if (this.unblock) this.unblock();
  this.unblock = this.context.router.history.block(message);
}

Link Component

Link renders an <a> element that intercepts clicks, prevents default navigation, and uses the router's history to push or replace the target location.

handleClick = event => {
  if (this.props.onClick) this.props.onClick(event);
  if (!event.defaultPrevented && event.button === 0 && !this.props.target && !isModifiedEvent(event)) {
    event.preventDefault();
    const { history } = this.context.router;
    const location = typeof to === 'string' ? createLocation(to, null, null, history.location) : to;
    const href = history.createHref(location);
    return (
);
  }
}

withRouter Higher‑Order Component

withRouter wraps a component with a Route that injects routing props ( history , location , match ) and forwards refs using hoist‑non‑react‑statics to preserve static properties.

const withRouter = Component => {
  const C = props => {
    const { wrappedComponentRef, ...remainingProps } = props;
    return (
(
)} />
    );
  };
  C.displayName = `withRouter(${Component.displayName || Component.name})`;
  C.WrappedComponent = Component;
  C.propTypes = { wrappedComponentRef: PropTypes.func };
  return hoistStatics(C, Component);
};

The article concludes with reference links to the official React Router API documentation and GitHub repository.

frontendJavaScriptReactRoutingSPAReact Router
Qunar Tech Salon
Written by

Qunar Tech Salon

Qunar Tech Salon is a learning and exchange platform for Qunar engineers and industry peers. We share cutting-edge technology trends and topics, providing a free platform for mid-to-senior technical professionals to exchange and learn.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.