Utility for integrating react-swipeable-views with react-router

react-swipeable-routes

Utility for integrating react-swipeable-views with react-router.

Example

Notice how the url changes when swipping.

This example is available on /example.

Usage

import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import SwipeableRoutes from "react-swipeable-routes";

const RedView = () => (
  <div style={{ height: 300, backgroundColor: "red" }}>Red</div>
);
const BlueView = () => (
  <div style={{ height: 300, backgroundColor: "blue" }}>Blue</div>
);
const GreenView = () => (
  <div style={{ height: 300, backgroundColor: "green" }}>Green</div>
);
const YellowView = () => (
  <div style={{ height: 300, backgroundColor: "yellow" }}>Yellow</div>
);

class App extends Component {
  render() {
    return (
      <Router>
        <div className="App">
          <div className="App-header">
            <img src={logo} className="App-logo" alt="logo" />
            <h2>React Swipeable Routes example</h2>
          </div>

          <div>
            <Link to="/red">Red</Link> |<Link to="/blue">Blue</Link> |
            <Link to="/green">Green</Link> |<Link to="/yellow">Yellow</Link>
          </div>

          <SwipeableRoutes>
            <Route path="/red" component={RedView} />
            <Route path="/blue" component={BlueView} />
            <Route path="/green" component={GreenView} />
            <Route path="/yellow" component={YellowView} />
          </SwipeableRoutes>
        </div>
      </Router>
    );
  }
}

export default App;

Any additional props will be passed down to SwipeableViews:

<SwipeableRoutes containerStyle={{"height: 100%"}}>
  <Route path="/red" component={RedView} />
  <Route path="/blue" component={BlueView} />
  <Route path="/green" component={GreenView} />
  <Route path="/yellow" component={YellowView} />
</SwipeableRoutes>

Replace url instead of push

You can add a replace prop to SwipeableRoutes and it will replace location instead of pushing it when swiping.

<SwipeableRoutes replace containerStyle={{"height: 100%"}}>
  <Route path="/red" component={RedView} />
  <Route path="/blue" component={BlueView} />
  <Route path="/green" component={GreenView} />
  <Route path="/yellow" component={YellowView} />
</SwipeableRoutes>

Scroll to top on swipe

class App extends Component {
  scrollToTop = index => {
    Array.from(this.el.containerNode.children).forEach((child, i) => {
      if (index !== i) {
        child.scrollTo(0, 0);
      }
    });
  };

  render() {
    return (
      <Router>
        <div className="App">
          <SwipeableRoutes
            replace
            onChangeIndex={this.scrollToTop}
            innerRef={el => (this.el = el)}
            containerStyle={{ height: 200 }}
          >
            <Route path="/red" component={RedView} />
            <Route path="/blue" component={BlueView} />
          </SwipeableRoutes>
        </div>
      </Router>
    );
  }
}

Routes with parameters

You can include routes with parameters in the path. However, you can't swipe to them directly, you have to enter through a link or a url change. If you want to be able to swipe to them, you can include a defaultParams props specifying the default parameters for when swipping to them.

import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import SwipeableRoutes from "react-swipeable-routes";

const RedView = () => (
  <div style={{ height: 300, backgroundColor: "red" }}>Red</div>
);
const BlueView = () => (
  <div style={{ height: 300, backgroundColor: "blue" }}>Blue</div>
);
const GreenView = () => (
  <div style={{ height: 300, backgroundColor: "green" }}>Green</div>
);
const YellowView = () => (
  <div style={{ height: 300, backgroundColor: "yellow" }}>Yellow</div>
);
const OtherColorView = ({ match }) => (
  <div style={{ height: 300, backgroundColor: match.params.color }}>
    {match.params.color}
  </div>
);

class App extends Component {
  render() {
    return (
      <Router>
        <div className="App">
          <div className="App-header">
            <img src={logo} className="App-logo" alt="logo" />
            <h2>React Swipeable Routes example</h2>
          </div>

          <div>
            <Link to="/red">Red</Link> |<Link to="/blue">Blue</Link> |
            <Link to="/green">Green</Link> |<Link to="/yellow">Yellow</Link> |
            <Link to="/other/palevioletred">Pale Violet Red</Link> |
            <Link to="/other/saddlebrown">Saddle Brown</Link>
          </div>

          <SwipeableRoutes>
            <Route path="/red" component={RedView} />
            <Route path="/blue" component={BlueView} />
            <Route path="/green" component={GreenView} />
            <Route path="/yellow" component={YellowView} />
            <Route
              path="/other/:color"
              component={OtherColorView}
              defaultParams={{ color: "grey" }}
            />
          </SwipeableRoutes>
        </div>
      </Router>
    );
  }
}

export default App;

Unlike react-router, with react-swipeable-routes all routes have to be rendered at all times. Because of that, unlike react-router, in which your component gets a match object only if there was a match, here your rendered component will always receive a match prop with same structure as the match prop in react-router except for one difference: it includes a type object indicating what type of match it is:

  • none: The view was never rendered and its parameters are the specified defaults.
  • outOfView: The view has different parameters than default, but it's currently out of the screen.
  • full: The view is currently on screen an it has different parameters than default.

GitHub