Overview

Overview demo built with Expressive MVC - the complete source below runs as an editable sandbox when JavaScript is enabled.

App.tsx

import './App.css';

import { Component, get } from '@expressive/react';
import { Link, Route, Router } from '@expressive/router';

// Routes ARE Components. The layout renders the nav once; the matched
// child arrives as `props.children`.
class Layout extends Component {
  render() {
    return (
      <div className="container">
        <h1>Router</h1>
        <nav className="nav">
          <Link to="/">Home</Link>
          <Link to="/about">About</Link>
          <Link to="/user/ada">User</Link>
        </nav>
        <div className="view">{this.props.children}</div>
      </div>
    );
  }
}

class Home extends Component {
  render() {
    return <p>Welcome. Pick a link - navigation is in-memory here.</p>;
  }
}

class About extends Component {
  render() {
    return <p>Each view is its own Component, matched by its Route.</p>;
  }
}

// A page reads its own Route from context for params, match, navigation.
class User extends Component {
  route = get(Route);

  render() {
    return (
      <p>
        Param <code>name</code> = <b>{this.route.match?.name}</b>
      </p>
    );
  }
}

export default () => (
  <Router>
    <Route as={Layout}>
      <Route as={Home} />
      <Route to="about" as={About} />
      <Route to="user/:name" as={User} />
    </Route>
  </Router>
);

App.css

.nav {
  display: flex;
  gap: 0.75rem;
  margin-bottom: 1rem;
  border-bottom: 1px solid var(--accent-soft);
  padding-bottom: 0.75rem;
}

.nav a {
  color: var(--accent);
  text-decoration: none;
  cursor: pointer;
}

.nav a:hover {
  text-decoration: underline;
}

.view {
  color: var(--fg);
}