Adam Hunter

Made with in NYC

Let's build a React app with user account functionality. Part I

Build a React frontend with the API from the previous post

2.25.22

With the API that we built in the previous post, let’s keep going and build a frontend with React.

To make sure all syntax and dependencies are up to date, I will start this from scratch. In part 1, I will show you exactly how to create a React app and get everything set up and looking nice. I will use Bootstrap for styling and React Router for the SPA (single page application) feel. In part 2, I will implement Redux and finish the functionality.

You don’t need to use a React toolchain to write React code, but using them is standard practice for a reason. Create React App is an awesome tool to get a React boilerplate set up for a frontend build using a Django (or any) backend. Since this is JavaScript, Create React App is a Node.js application and will require Node.js (version 14 or higher) to be installed. Node.js comes with nvm (node version manager) to change to a specific version of node and to update it.

$ # check node version
$ node -v
$ # install latest version of node
$ nvm install node
$ # create react app
$ npx create-react-app <app-name>
$ # make sure it worked
$ # change into newly created app directory
$ cd <app-name>
$ # start development server
$ npm start



If that was successful, the React app will be available to view in a browser at http://localhost:3000 or on any device on the network at port 3000.

Now is when I usually like to do some small customizations, like change out the favicon, change the page title in project-name/public/index.html, set up tests, get rid of the boilerplate CSS and the stock code in project-name/src/App.js.

Next I like to make the components and screens directories. The screens are technically React components also, but it is good practice to stay as organized as possible. When you have a lot of components and screens, this makes finding your files easier.


$ # control + D (Mac/Linux) will stop the dev server
$ # change into the src directory
$ cd src
$ # create directories for components and screens
$ mkdir components screens
$ # change into new components directory
$ cd components
# create a JavaScript file for the header component
$ touch Header.js 
$ # change into new screens directory
$ cd ../screens 
# create JavaScript files for the Home, Login and Register screens
$ touch HomeScreen.js LoginScreen.js RegisterScreen.js
$ # change back to src directory
$ cd ..
$ # open the src folder
$ open .



I’m going to use React Bootstrap components and a Bootswatch theme. Download the bootstrap.min.css file from theme of your choice and add it to the src folder that we just opened. Import the new CSS file in index.js:

import './bootstrap.min.css'



Hit save and that should theme the app.

I like to make basic exported React functional components in the new screens and components so they aren’t blank files. Something as simple as this:

import React from 'react'
function RegisterScreen() {
  return (
    <div>RegisterScreen</div>
  )
}
export default RegisterScreen



Now we can install React Bootstrap.

$ # change to project root directory
$ cd ..
$ # install react bootstrap
$ npm install react-bootstrap
$ # start dev server again
$ npm start



Configure App.js with the HomeScreen and Header components, move it into a React Bootstrap Container. We are going to import it, add a JSX main tag to define the body, add some Bootstrap padding to it, and then wrap the body in the Container:

import { Container } from 'react-bootstrap'
import Header from './components/Header'
function App() {
  return (
    <div>
        <Header />
            <main className="py-3">>
                <Container>
                    <HomeScreen />
                </Container>
            </main>
    </div>
  );
}
export default App;



Since we already added the exported functional components to the Header and HomeScreen, we won’t get an error with the dev server running and now we will see the changes immediately as we build them.

One thing that makes React apps so lightning fast is not reloading the app to navigate to another page. This is accomplished by changing regular href links to React Router links. Since we are using React Bootstrap, we want to use react-router-bootstrap so our links will still be styled by the CSS. So we need to install both:

$ # control + D will stop the dev server
$ # install react-router-dom
$ npm install react-router-dom
$ # install react-router-bootstrap
$ npm install react-router-bootstrap
$ # turn server back on
$ npm start



React Router v6 is the latest version and to set it up you need to add Routes and Route paths to App.js. Let’s import the other two screens and set the url routing:

import { Routes, Route, Link } from "react-router-dom";
import { Container } from 'react-bootstrap'
import Header from './components/Header'
import HomeScreen from './screens/HomeScreen'
import LoginScreen from './screens/LoginScreen'
import RegisterScreen from './screens/RegisterScreen'
function App() {
  return (
    <div>
    
      <Header />
      <main className="py-3">
        <Container>
          <Routes>
            <Route path="/" element={<HomeScreen />} />
            <Route path="login" element={<LoginScreen />} />
            <Route path="register" element={<RegisterScreen />} />
          </Routes>
          {/* <HomeScreen /> */}
        </Container>
      </main>
    
    </div>
  );
}
export default App;



To finish setting up React Router make a quick import and edit to index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from "react-router-dom";
import './index.css';
import './bootstrap.min.css'
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();



I think Font Awesome Icons are cool. They look good, have a ton of free ones, and are easy to use. To be able to use Font Awesome Icons:

Get the font-awesome CDN link and add it to project-name/public/index.html and then just add the i tag where you want the icon. In index.html:

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" integrity="sha512-9usAa10IRO0HhonpyAIVpjrylPvoDwiPUiKdWk5t3PyolY1cOd4DSE0Ga+ri4AuTroPR5aQvXU9xC6qOPnzFeg==" crossorigin="anonymous" referrerpolicy="no-referrer" />



Ok, let’s finally get into Header.js.

Change the div to a header tag. Grab some Bootstrap Navbar code to customize. I am using the off-canvas menu. Change href links to Link Containers to get that SPA (single page application) feel.

import React, { useState } from 'react'
import { Navbar, Nav, Container, NavDropdown, Form, FormControl, Button } from 'react-bootstrap'
import { LinkContainer } from 'react-router-bootstrap'
import Offcanvas from 'react-bootstrap/Offcanvas'
function Header() {
  return (
    <header>
        <Navbar bg="black" variant="dark" expand={false}>
            <Container fluid>
                <LinkContainer to='/'>
                    <Navbar.Brand>        
                        <img
                            alt=""
                            src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/240/apple/285/fire_1f525.png"
                            width="30"
                            height="30"
                            className="d-inline-block align-top"
                        />{' '}
                        App Name
                    </Navbar.Brand>
                 </LinkContainer>
                 <Navbar.Toggle aria-controls="offcanvasNavbar" />
                 <Navbar.Offcanvas 
                    id="offcanvasNavbar"
                    aria-labelledby="offcanvasNavbarLabel"
                    placement="end"
                >
            <Offcanvas.Header closeButton>
                <Offcanvas.Title id="offcanvasNavbarLabel">Offcanvas Menu Title</Offcanvas.Title>
            </Offcanvas.Header>
            <Offcanvas.Body>
                <Nav className="justify-content-end flex-grow-1 pe-3">
                <LinkContainer to='/'>
                    <Nav.Link><i class="fa-solid fa-house-chimney"></i>  Home</Nav.Link>
                </LinkContainer>
                <LinkContainer to='login'>
                    <Nav.Link><i class="fa-solid fa-skull"></i>  Log in</Nav.Link>
                </LinkContainer>
                </Nav>
                <Form className="d-flex">
                    <FormControl
                        type="search"
                        placeholder="Search"
                        className="me-2"
                        aria-label="Search"
                    />
                    <Button variant="outline-success">Search</Button>
                </Form>
            </Offcanvas.Body>
            </Navbar.Offcanvas>
             </Container>
        </Navbar>
    </header>
  )
}
export default Header



Before we build out the LoginScreen and RegisterScreen, create FormContainer.js in src/components.

import React from 'react'
import { Container, Row, Col } from 'react-bootstrap'
function FormContainer({ children }) {
    return (
        <Container>
            <Row className="justify-content-md-center">
                <Col xs={12} md={6}>
                    {children}
                </Col>
            </Row>
        </Container>
    )
}
export default FormContainer



Now we can use the FormContainer we just built along with React Bootstrap Form, Button, Row, Col, and Card components for the LoginScreen and RegisterScreen.

LoginScreen.js

import React, { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import { Form, Button, Row, Col, Card } from 'react-bootstrap'
import FormContainer from '../components/FormContainer'
function LoginScreen({ location, history }) {
    return (
        <Card 
            className="text-center"
            >
        <FormContainer>
        <Card.Header as="h3"><i class="fa-solid fa-skull"></i> Sign In</Card.Header>
        <br />
            <Form>
                <Form.Group controlId='email'>
                    <Form.Label><i class="fas fa-envelope"></i> Email Address</Form.Label>
                    <Form.Control
                        type='email'
                        placeholder='Enter Email'
                    >
                    </Form.Control>
                </Form.Group>
                <br />
                <Form.Group controlId='password'>
                    <Form.Label><i class="fas fa-key"></i> Password</Form.Label>
                    <Form.Control
                        type='password'
                        placeholder='Enter Password'
                    >
                    </Form.Control>
                </Form.Group>
                <br />
                <Button type='submit' variant='dark'>Sign in</Button>
            </Form>
            <Row className='py-3'>
                <Col>
                    Don't have an account yet?  <Link to={'/register'}>Make one here.</Link>
                </Col>
            </Row>
        </FormContainer>
        </Card>
    )
}
export default LoginScreen



RegisterScreen.js

import React, { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import { Form, Button, Row, Col, Card } from 'react-bootstrap'
import FormContainer from '../components/FormContainer'

function RegisterScreen() {
    return (
        <Card 
            className="text-center"
            >
        <FormContainer>
            <Card.Header as="h3"><i class="fa-solid fa-skull"></i> Create An Account</Card.Header>
            <br />
            <Form>
            
                <Form.Group controlId='name'>
                    <Form.Label><i className="fas fa-user"></i> Name</Form.Label>
                        <Form.Control
                            required
                            type='name'
                            placeholder='Enter Your Name'
                        >
                    </Form.Control>
                </Form.Group>
                <br />
                <Form.Group controlId='email'>
                    <Form.Label><i class="fas fa-envelope"></i> Email Address</Form.Label>
                        <Form.Control
                            required
                            type='email'
                            placeholder='Enter Email'
                        >
                    </Form.Control>
                </Form.Group>
                <br />
                <Form.Group controlId='password'>
                    <Form.Label><i class="fas fa-key"></i> Password</Form.Label>
                    <Form.Control
                        required
                        type='password'
                        placeholder='Enter Password'
                    >
                    </Form.Control>
                </Form.Group>
                <br />
                <Form.Group controlId='passwordConfirm'>
                    <Form.Label><i class="fas fa-key"></i> Confirm Password</Form.Label>
                    <Form.Control
                        required
                        type='password'
                        placeholder='Enter Password'
                    >
                    </Form.Control>
                </Form.Group>
                <br />
                <Button type='submit' variant='dark'>Register</Button>
            </Form>
            <Row className='py-3'>
                <Col>
                    Already have an account? <Link to={'/login'}>Sign in here.</Link>
                </Col>
            </Row>
        </FormContainer>
        </Card>
    )
}
export default RegisterScreen



If all has gone well up to this point you should have an app up that looks like this:



I think this is a good stopping point for now. In the next post, we will wire up this app to the Django app from the previous post. I think we will end up with some super useful and reusable code for a full-stack project.

Adamadam hi

Adam