Building a MERN Survey App, Day 2: Routing and Bootstrap

Building a MERN Survey App, Day 2: Routing and Bootstrap

Today will be spent working on the front end in React. Today's goals are to get the routing working, integrate Bootstrap, and display a form.

Project Structure

Inside the src folder are two folders titled components and routes, along with an index.js file. The components folder holds Nav.jsx and the routes folder holds App.jsx, Home.jsx, Survey.jsx, and Dashboard.jsx. Project structure

Basic Routing

// index.js

import ReactDOM from "react-dom/client";
import {
    BrowserRouter,
    Routes,
    Route
} from "react-router-dom";
import App from "./routes/App";
import Survey from "./routes/Survey";
import Dashboard from "./routes/Dashboard";
import Home from "./routes/Home";


const root = ReactDOM.createRoot(document.getElementById("root"));

root.render(
    <BrowserRouter>
        <Routes>
            <Route path="/" element={<App />}>
                <Route index element={<Home />} />
                <Route path="survey" element={<Survey />} />
                <Route path="dashboard" element={<Dashboard />} />
                <Route path="*" element={<p>There's nothing here!</p>} />
            </Route>
        </Routes>
    </BrowserRouter>
);

This sets up nested routing as follows:

  • App is the top-level element and because all other routes are nested within "/", App serves as a template for the entire site.
  • All other routes are defined relative to "/" because they're nested, so they do not include a leading slash in their route.
  • the index route defines the content that will only be displayed when the user accesses the parent route (in this case, it's '/', which in my case is currently localhost:3000)
  • path="*" defines what should be shown when the user tries to access a URL with no matching route. Note that this does NOT return a 404 status code; I am going to ignore that problem for now.
// App.jsx

import { Outlet } from "react-router-dom";
import Nav from '../components/Nav';

export default function App() {
    return (
        <div>
            <Nav />
            <Outlet />
        </div>
    );
}

Currently, App serves as an extremely basic template: it renders the Nav component on every page, and that's it. The Outlet component is provided by React Router, and defines the spot where elements from nested routes will be rendered. If you've ever used a template engine or static site generator, it works something like {{ content }} or {% block content %}{% endblock %}.

// Nav.jsx
import { Link } from "react-router-dom";

export default function Nav() {
    return (
        <nav>
            <Link to="/">Home</Link> |{" "}
            <Link to="/survey">Survey</Link> |{" "}
            <Link to="/dashboard">Dashboard</Link>
        </nav>
    )
}

Nav is currently just a few links to go from page to page. The Link element is provided by React Router and serves the same function as an <a> tag. However, Link will not refresh the page but <a> will.

// Home.jsx
export default function Home() {
    return (
        <h1>Home</h1>
    )
}
// Survey.jsx
export default function Survey() {
    return (
        <h1>Survey</h1>
    )
}
// Dashboard.jsx
export default function Dashboard() {
    return (
        <h1>Dashboard</h1>
    )
}

Home.jsx is the index route for "/", Survey.jsx and Dashboard.jsx are the elements to be rendered for their respective routes, nested inside "/" (as defined in index.js).

This sets up the basic structure for the site and produces this rather unimpressive result:

You can see in the bottom left that the URLs are changing for each page, but the page doesn't refresh when the links are clicked. Although it doesn't look like much, this does provide the bones needed for the next steps.

Bootstrap

I am using Reactstrap to add Bootstrap to my project. This isn't the only way to add Bootstrap to React, but it does add Bootstrap components as React components, which I like. I have already installed Reactstrap and Bootstrap, so the only thing left to do to get it up and running is import the CSS by adding this import to index.js:

import 'bootstrap/dist/css/bootstrap.css';

Website before and after Bootstrap Bootstrap is working, so now it's time to start adding some components and styling.

The first thing I wanted to do was add a Bootstrap Navbar to the Nav component I had created, but I immediately ran into a couple of problems:

  1. Bootstrap also has a component named Nav which was conflicting with my function declaration. I could have give the Boostrap Nav an alias but I thought it was easier (and a little more semantic) to simply rename my Nav component to Header.
  2. The Link component from React Router will not track which page you're currently on, you have to use NavLink for that, but NavLink already exists in Reactstrap. I solved this by giving React Router's NavLink an alias:
    import { NavLink as RRNavLink } from 'react-router-dom';
    
  3. The NavLink component provided by Reactstrap works like a regular <a> tag - that is, it refreshes the page when clicked. Plus, it is not aware of the page you're currently on and therefore can't highlight it in the nav, since it can't automatically add an 'active' class without that information. React Router's NavLink does have this information as I mentioned above, but directly using the NavLink from React Router means I lose access to the Bootstrap styling. What's needed is a way to get them to work together. Fortunately, one already exists: use tag={RRNavLink} on the Bootstrap NavLink and then you can use it as a React Router NavLink while maintaining the Bootstrap styling.
// Header.jsx (previously Nav.jsx)
import { NavLink as RRNavLink } from 'react-router-dom';
import {
    Navbar,
    Nav,
    NavItem,
    NavLink
} from 'reactstrap';

export default function Header() {
    return (
        <header>
            <Navbar>
                <Nav pills>
                    <NavItem>
                        <NavLink tag={RRNavLink} to="/" activeClassName="active">Home</NavLink>
                    </NavItem>
                    <NavItem>
                        <NavLink tag={RRNavLink} to="/survey" activeClassName="active">Survey</NavLink>
                    </NavItem>
                    <NavItem>
                        <NavLink tag={RRNavLink} to="/dashboard" activeClassName="active">Dashboard</NavLink>
                    </NavItem>
                </Nav>
            </Navbar>
        </header>
    )
}

The next step is to add a bit of layout to the site. This was trivial, since the layout is currently so simple. I didn't add Col elements in App so that they can be added in nested elements as needed.

// App.jsx

import { Outlet } from "react-router-dom";
import { Container, Row } from 'reactstrap';
import Header from '../components/Header';


export default function App() {
    return (
        <Container>
            <Row>
                <Header />
            </Row>

            <Row>
                <Outlet />
            </Row>
        </Container>

The app after adding Bootstrap layout.

A Form

The last step for today is to add a form to the survey page. Reactstrap includes Bootstrap form components, and since I'm not hooking the form up to anything just yet, this is another easy task.

// Survey.jsx

import {
    Row,
    Col,
    Form,
    FormGroup,
    Label,
    Input,
    FormText,
    Button

} from 'reactstrap';

export default function Survey() {
    return (
        <Col>
        <h1>Survey</h1>
            <Form>
                <Row>
                    <Col>
                        <FormGroup>
                            <Label for="exampleName">
                                Name
                            </Label>
                            <Input
                                id="exampleName"
                                name="name"
                                placeholder="Name"
                                type="text"
                            />
                        </FormGroup>
                    </Col>
                    <Col>
                        <FormGroup>
                            <Label for="exampleEmail">
                                Email
                            </Label>
                            <Input
                                id="exampleEmail"
                                name="email"
                                placeholder="Email"
                                type="email"
                            />
                        </FormGroup></Col></Row>
                <Row>
                    <Col>
                        <FormGroup>
                            <Label for="examplePassword">
                                Password
                            </Label>
                            <Input
                                id="examplePassword"
                                name="password"
                                placeholder="Password"
                                type="password"
                            />
                        </FormGroup>
                    </Col>
                    <Col>
                        <FormGroup>
                            <Label for="exampleConfirm">
                                Confirm
                            </Label>
                            <Input
                                id="exampleConfirm"
                                name="password"
                                placeholder="Type password again"
                                type="password"
                            />
                        </FormGroup>
                    </Col>
                </Row>
                <Row>
                    <Col>
                        <FormGroup>
                            <Label for="exampleSelect">
                                Select
                            </Label>
                            <Input
                                id="exampleSelect"
                                name="select"
                                type="select"
                            >
                                <option>1</option>
                                <option>2</option>
                                <option>3</option>
                                <option>4</option>
                                <option>5</option>
                            </Input>
                        </FormGroup>
                    </Col>
                    <Col>
                        <FormGroup>
                            <Label for="exampleSelectMulti">
                                Select Multiple
                            </Label>
                            <Input
                                id="exampleSelectMulti"
                                multiple
                                name="selectMulti"
                                type="select"
                            >
                                <option>1</option>
                                <option>2</option>
                                <option>3</option>
                                <option>4</option>
                                <option>5</option>
                            </Input>
                        </FormGroup>
                    </Col>
                    <Col>
                        <FormGroup>
                            <Label for="exampleText">
                                Text Area
                            </Label>
                            <Input
                                id="exampleText"
                                name="text"
                                type="textarea"
                            />
                        </FormGroup>
                    </Col>
                </Row>
                <FormGroup>
                    <Label for="exampleFile">
                        File
                    </Label>
                    <Input
                        id="exampleFile"
                        name="file"
                        type="file"
                    />
                    <FormText>
                        This is some placeholder block-level help text for the above input. It's a bit lighter and easily wraps to a new line.
                    </FormText>
                </FormGroup>
                <FormGroup tag="fieldset">
                    <legend>
                        Radio Buttons
                    </legend>
                    <FormGroup check>
                        <Input
                            name="radio1"
                            type="radio"
                        />
                        {' '}
                        <Label check>
                            Option one is this and that—be sure to include why it‘s great
                        </Label>
                    </FormGroup>
                    <FormGroup check>
                        <Input
                            name="radio1"
                            type="radio"
                        />
                        {' '}
                        <Label check>
                            Option two can be something else and selecting it will deselect option one
                        </Label>
                    </FormGroup>
                    <FormGroup
                        check
                        disabled
                    >
                        <Input
                            disabled
                            name="radio1"
                            type="radio"
                        />
                        {' '}
                        <Label check>
                            Option three is disabled
                        </Label>
                    </FormGroup>
                </FormGroup>
                <FormGroup check>
                    <Input type="checkbox" />
                    {' '}
                    <Label check>
                        Check me out
                    </Label>
                </FormGroup>
                <Button>
                    Submit
                </Button>
            </Form>
        </Col>
    )
}
}

Rendered form

This is just an example form provided by Reactstrap with some rows and columns added to play around with a nested layout, but it should be sufficient for this part of the project. Tomorrow I'll start actually using some of React's features.

NOTE: This is not a tutorial. I am learning how to build a MERN app and decided to record my progress. Although I'm doing my very best to make sure my code is correct, I may not always be following best practices. If you see any mistakes, feel free to contact me at l@abrocadabro.com or leave a comment. I would love to know what I can do better!