Responsive routing in React involves serving different routes to users based on the viewport of their device. CSS media queries are usually used to achieve this, but this restricts you to either showing or not showing different elements by using the CSS props. With responsive routes, you can now serve entire separate views of your React applications to different users based directly on their screen sizes.
In this tutorial, we will show you how to implement routing and serving responsive routes in your React applications. By following this tutorial, you will build a user dashboard application that serves different routes to users based on the size of their device screens.
To complete this tutorial, you will need:
This tutorial was verified with Node v14.2.0, npm
v6.14.5, react
v16.3.2, react-router-dom
v5.2.0, and react-media
v1.10.0.
To start your project, use npx
and create-react-app
to create a new React application:
- npx create-react-app responsive-routing
Then, navigate to the new project directory:
- cd responsive-routing
Next, install the necessary modules you will need to successfully build this demo. These modules are the react-router-dom
and react-media
. You can install these by running the command:
- npm install react-router-dom@5.2.0 react-media@1.10.0
Now, you can start the application by running the command:
- npm start
Note: While not required for routing, this tutorial uses the Bulma CSS framework for styling and layout.
You can add Bulma with the following terminal command:
- npm install bulma@0.6.2
And by adding the following to your index.js
:
import 'bulma/css/bulma.css';
In this step you’ve set up your project and added the Bulma framework for styling and layout.
To add routing to your project, you will need to modify your index.js
file to render the router at the root of your element hierarchy:
- nano index.js
First, import BrowserRouter
from react-router-dom
and alias it to Router
:
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router } from "react-router-dom";
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
Then, replace <React>
with <Router>
:
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById('root')
);
Your application is now set up to use React Router.
Nav
ComponentThe GitHub logo in the center of the page will serve as the navigation part of your application.
In your src
directory, create a new directory called Nav
:
- mkdir src/Nav
You will need to add the GitHub logo and save it as logo.svg
in this directory.
Next, create an index.js
file in this directory:
- nano src/Nav/index.js
And add the following code:
import React from 'react';
import './Nav.css';
import logo from './logo.svg';
const Nav = () => (
<nav>
<img src={logo} alt="Logo" />
</nav>
);
export default Nav;
Next, create a Nav.css
file in this directory:
- nano src/Nav/Nav.css
The navigation component has the following styling:
nav {
display: flex;
justify-content: center;
height: 50px;
margin-bottom: 10px;
}
nav > img {
display: block;
width: 50px;
height: auto;
}
Now, let’s render the Nav
component by modifying the App.js
file.
- nano src/App.js
Import the Nav
component and use it in your App
component:
import React, { Component } from 'react';
import Nav from './Nav';
class App extends Component {
render() {
return (
<div>
<Nav />
</div>
);
}
}
export default App;
Now, when you open your app in a web browser, you should see the logo you added.
UsersCard
ComponentThe user cards will be responsible for displaying details of the user. It will contain information like an avatar
, name
, and username
. It will also display followers
, following
, and repos
.
In the src
directory of your app, create a new Users
directory:
- mkdir src/Users
Next, create a UsersCard.js
file in this directory:
- nano src/Users/UsersCard.js
And add the following code:
import React from 'react';
import { Link } from 'react-router-dom';
import './UsersCard.css'
const UsersCard = ({ user, match }) => <Link to={`${match.url}/${user.id}`} className="column card">
<img src={user.avatar} alt=""/>
<p className="users-card__name">{user.name}</p>
<p className="users-card__username">@{user.username}</p>
<div className="users-card__divider"></div>
<div className="users-card__stats">
<div>
<p>{user.followers}</p>
<span>Followers</span>
</div>
<div>
<p>{user.following}</p>
<span>Following</span>
</div>
<div>
<p>{user.repos}</p>
<span>Repositories</span>
</div>
</div>
</Link>;
export default UsersCard;
The Link
component from react-router-dom
is used to allow the user to navigate to view details of a single user when the card is clicked.
For example, if a UsersCard
has an id
of 10009
, the Link
component will generate a URL like this:
localhost:3000/10009
localhost:3000
represents the current URL.10009
represents the $user.id
.All this information will be passed when the component is rendered.
Next, create a UsersCard.css
file in this directory:
- nano src/users/UsersCard.css
The UsersCard
component has the following styling:
.card {
border-radius: 2px;
background-color: #ffffff;
box-shadow: 0 1.5px 3px 0 rgba(0, 0, 0, 0.05);
max-width: 228px;
margin: 10px;
display: flex;
flex-direction: column;
align-items: center;
padding: 0;
}
.card img {
width: 50px;
height: auto;
border-radius: 50%;
display: block;
padding: 15px 0;
}
.users-card__name {
font-weight: 400;
font-size: 16.5px;
line-height: 1.19;
letter-spacing: normal;
text-align: left;
color: #25292e;
}
.users-card__username {
font-size: 14px;
color: #707070;
}
.users-card__divider {
border: solid 0.5px #efefef;
width: 100%;
margin: 15px 0;
}
.users-card__stats {
display: flex;
}
.users-card__stats p {
font-size: 20px;
}
.users-card__stats div {
margin: 10px;
text-align: center;
}
.users-card__stats span {
color: #707070;
font-size: 12px;
}
At this point you have a UsersCard
component. Next, you will need to display theses cards in a list.
UsersList
ComponentTo get your application to list users, you will need to first create a UsersList
component.
Create a UsersCard.js
file in the src/Users
directory:
- nano UsersList.js
Let’s edit the UsersList.js
as follows.
First, you will make the necessary imports:
import React from 'react';
import UsersCard from './UsersCard';
import './UsersList.css';
Define a listOfUsersPerRow
function that will build out a UsersCard
coinciding with their position in the array of users
:
// ...
const listOfUsersPerRow = (users, row, itemsPerRow, match) =>
users
.slice((row - 1) * itemsPerRow, row * itemsPerRow)
.map(user => <UsersCard user={user} key={user.id} match={match} />);
Define a listOfRows
function that will build out "columns"
containing UsersCard
s defined by the amount of itemsPerRow
:
// ...
const listOfRows = (users, itemsPerRow, match) => {
const numberOfUsers = users.length;
const rows = Math.ceil(numberOfUsers / itemsPerRow);
return Array(rows)
.fill()
.map((val, rowIndex) => (
<div className="columns">
{listOfUsersPerRow(users, rowIndex + 1, itemsPerRow, match)}
</div>
));
};
The listOfUsersPerRow
and listOfRows
functions ensure that you have no more than the specified number of cards on each row.
Then, use the functions to create a UsersList
:
//...
const UsersList = ({ users, itemsPerRow = 2, match }) => (
<div className="cards">
<h3 className="is-size-3 has-text-centered">Users</h3>
{listOfRows(users, itemsPerRow, match)}
</div>
);
export default UsersList;
Next, create a UsersList.css
file in this directory:
- nano src/Users/UsersList.css
The UsersList
component has the following styling:
.cards {
margin-left: 20px;
}
.columns {
margin-top: 0;
}
At this point, you have a UsersList
component consisting of UsersCard
s. Next, you will need a detailed view for an individual user.
UsersDetails
ComponentWhen a single UsersCard
is clicked from the UsersList
, the single UsersCard
is displayed under a details section.
Create a UsersDetails.js
file in the src/Users
directory:
- nano UsersDetails.js
And add the following code:
import React from 'react';
import UsersCard from './UsersCard';
const UsersDetails = ({ user, match }) => <div>
<h3 className="is-size-3 has-text-centered">Details</h3>
<UsersCard user={user} match={match} />
</div>;
export default UsersDetails;
At this point, you have a UsersDetails
component. Next, you will display UsersLists
and UsersDetails
.
UsersDashboard
ComponentTo make the dashboard component, you will display the UsersList
and, when a UsersCard
is clicked, display the UsersDetails
on the side of the screen without having to reload the page.
Create a UsersDashboard.js
file in the src/Users
directory:
- nano src/Users/UsersDashboard.js
And add the following code:
import React from 'react';
import { Route } from 'react-router-dom';
import UsersList from './UsersList';
import UsersDetails from './UsersDetails';
const UsersDashboard = ({ users, user, match }) => (
<div className="columns">
<div className="column">
<UsersList users={users} match={match} />
</div>
<div className="column">
<Route
path={match.url + '/:id'}
render={props => (
<UsersDetails
user={
users.filter(
user => user.id === parseInt(props.match.params.id, 10)
)[0]
}
match={match}
/>
)}
/>
</div>
</div>
);
export default UsersDashboard;
In this snippet, you used the Route
component provided by react-router-dom
as a component to display the specific user detail when the card is clicked.
At this point, you have all the components for your application.
Now, let’s put this all together.
Revisit the App.js
file:
- nano src/App.js
Add Redirect
and UsersDashboard
:
import React, { Component } from 'react';
import { Route, Redirect } from 'react-router-dom';
import Nav from './Nav';
import UsersDashboard from './Users/UsersDashboard';
import './App.css';
Add state
containing an array of users
:
//...
class App extends Component {
state = {
users: [
{
id: 39191,
avatar: 'https://avatars0.githubusercontent.com/u/39191?v=4',
name: 'Paul Irish',
username: 'paulirish',
followers: '12k',
following: '1k',
repos: '1.5k'
},
// ... other user data
]
};
// ...
}
// ...
Add Route
and UsersDashboard
to your App
component:
class App extends Component {
// ...
render() {
return (
<div className="App">
<Nav />
<Route
path="/dashboard"
render={props => (
<UsersDashboard users={this.state.users} {...props} />
)}
/>
<Redirect from="/" to="/dashboard"/>
<Redirect from="/users" to="/dashboard"/>
</div>
);
}
}
// ...
Now, when viewing your application in a web browser, you should see a UsersList
. When clicking on a UsersCard
, you will see it display in the UsersDetails
.
When users visit this application, no matter the screen size, they get this same view and functionality. In full-blown applications, it’s good to give the users experiences they can enjoy properly. One way to do that is to serve them views that match their exact device sizes. You are now going to implement this in your application.
When visiting the application on a wide screen, the user is redirected to the /dashboard
route of the application, and when viewing on a smaller screen, the user will be directed to the /users
route of the application.
Update the src/App.js
file to look like this:
import React, { Component } from 'react';
import { Route, Switch, Redirect } from 'react-router-dom'; // add Switch
import Media from 'react-media'; // add Media
import Nav from './Nav';
import UsersList from './Users/UsersList'; // add UsersList
import UsersDetails from './Users/UsersDetails'; // add UsersDetails
import UsersDashboard from './Users/UsersDashboard';
import './App.css';
class App extends Component {
// ...
render() {
return (
<div className="App">
<Nav />
<Media query="(max-width: 599px)">
{matches =>
matches ? (
<Switch>
<Route
exact
path="/users"
render={props => (
<UsersList users={this.state.users} {...props} />
)}
/>
<Route
path="/users/:id"
render={props => (
<UsersDetails
user={
this.state.users.filter(
user =>
user.id === parseInt(props.match.params.id, 10)
)[0]
}
{...props}
/>
)}
/>
<Redirect from="/" to="/users"/>
<Redirect from="/dashboard" to="/users"/>
</Switch>
) : (
<Switch>
<Route
path="/dashboard"
render={props => (
<UsersDashboard users={this.state.users} {...props} />
)}
/>
<Redirect from="/" to="/dashboard"/>
<Redirect from="/users" to="/dashboard"/>
</Switch>
)
}
</Media>
</div>
);
}
}
export default App;
In this snippet, you used the Media
component to check the size of the screen. If the screen width is less than 599px
, you set what you wanted to be displayed for different routes and also redirect the /
and /dashboard
routes to the /users
route.
If the screen size is greater than 599px
, you display the full user dashboard as established in the previous step.
Run the application:
npm start
Interact with your application and adjust your screen size to see how routes are handled differently when interacting with the application.
Serving different routes based on screen sizes offers something beyond media queries, because you can now serve specially designed components to users based on their device sizes.
In this article, you were introduced to component-based routing with React and how to implement conditional rendering in your React applications.
For a complete code sample of this tutorial, check out the responsive-routing
repository on GitHub.
If you’d like to learn more about React, take a look at our How To Code in React.js series, or check out our React topic page for exercises and programming projects.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!