In this tutorial, we’re gonna build a React Typescript: Login and Registration example with React Router, Axios and Bootstrap (without Redux). I will show you:
- JWT Authentication Flow for User Signup & User Login
- Project Structure for React Typescript Authentication (without Redux) with React Router & Axios
- Creating React Components with Form Validation using Formik and Yup
- React Typescript Components for accessing protected Resources (Authorization)
- Dynamic Navigation Bar in React Typescript App
Related Posts:
– In-depth Introduction to JWT-JSON Web Token
– React Typescript CRUD example Project with Axios and Web API
– React Typescript File Upload example
Fullstack (JWT Authentication & Authorization example):
– React + Spring Boot
– React + Node Express + MySQL/PostgreSQL
– React + Node Express + MongoDB
Using Typescript with React Hooks instead:
React Typescript Authentication example with Hooks
React Custom Hook in Typescript example
Contents
- Overview of React Typescript Login example
- User Registration and User Login Flow
- React Typescript Component Diagram
- Technology
- Project Structure
- Setup React Typescript Project
- Import Bootstrap
- Add React Router
- Create Services
- Create React Components for Authentication
- Accessing Resources
- Add Navbar and define Routes
- Logout when the Token is expired
- Add CSS style
- Configure Port for React Client with Web API
- Conclusion
- Further Reading
Overview of React Typescript Login example
We will build a React Typescript application in that:
- There are Login/Logout, Signup pages.
- Form data will be validated by front-end before being sent to back-end.
- Depending on User’s roles (admin, moderator, user), Navigation Bar changes its items automatically.
Here are the screenshots:
– Signup Page:
– Form Validation Support:
– Login Page:
– Profile Page (for successful Login):
– For Moderator account login, the navigation bar will change by authorities:
– Check Browser Local Storage:
– Try to access unauthorized resource (Admin Page):
If you want to add refresh token, please visit:
React Refresh Token with JWT and Axios Interceptors
User Registration and User Login Flow
For JWT Authentication, we’re gonna call 2 endpoints:
- POST
api/auth/signup
for User Registration - POST
api/auth/signin
for User Login
The following flow shows you an overview of Requests and Responses that React Typescript Login Client will make or receive from Auth Server. This Client must add a JWT to HTTP Header before sending request to protected resources.
You can find step by step to implement these back-end servers in following tutorial:
- Spring Boot JWT Authentication with Spring Security, MySQL
- Spring Boot JWT Authentication with Spring Security, PostgreSQL
- Spring Boot JWT Authentication with Spring Security, MongoDB
- Node JWT Authentication & Authorization with MySQL
- Node JWT Authentication & Authorization with MongoDB
- Node JWT Authentication & Authorization with PostgreSQL
Demo Video
This is full React + Node Express JWT Authentication & Authorization demo (with form validation, check signup username/email duplicates, test authorization with 3 roles: Admin, Moderator, User):
And this is using Spring Boot Server:
In the videos above, we use React with Javascript. But the UI and logic and are the same as the React Typescript project in this tutorial.
React Typescript Login Component Diagram
Let’s look at the diagram below.
– The App
component is a container with React Router (BrowserRouter
). Basing on the state, the navbar can display its items.
– Login
& Register
components have form for data submission (with support of formik
and yup
library). They call methods from auth.service
to make login/register request.
– auth.service
uses axios
to make HTTP requests. Its also store or get JWT from Browser Local Storage inside these methods.
– Home
component is public for all visitor.
– Profile
component displays user information after the login action is successful.
– BoardUser
, BoardModerator
, BoardAdmin
components will be displayed by state user.roles
. In these components, we use user.service
to access protected resources from Web API.
– user.service
uses auth-header()
helper function to add JWT to HTTP header. auth-header()
returns an object containing the JWT of the currently logged in user from Local Storage.
Technology
We’re gonna use these modules:
- React 18/17/16
- typescript 4.4.2
- react-router-dom 6/5
- axios 0.27.2
- formik 2.2.9
- Bootstrap 4
- yup 0.32.11
Project Structure
This is folders & files structure for this React Typescript Login application:
With the explanation in diagram above, you can understand the project structure easily.
Additionally, EventBus
is for emitting Logout event when the Token is expired.
Setup React Typescript Login Project
Open cmd at the folder you want to save Project folder, run command:
npx create-react-app react-typescript-login-example --template typescript
Import Bootstrap to React Typescript Project
Run command:
– yarn add [email protected]
– Or: npm install [email protected]
.
Open src/App.tsx and modify the code inside it as following-
import { Component } from "react";
import "bootstrap/dist/css/bootstrap.min.css";
class App extends Component<Props, State> {
render() {
// ...
}
}
export default App;
Add React Router to React Typescript Login Project
When using Typescript with React, we don’t use Proptypes. Typescript is stronger than Propstypes.
npm
has many dependencies with prefix @types/{name}
such as @types/lodash
, @types/react
… which is easy to install and use. For this project, we use @types/react-router-dom
.
– Run the command: yarn add react-router-dom @types/react-router-dom
.
Or: npm install react-router-dom @types/react-router-dom
.
– Open src/index.tsx and wrap App
component by BrowserRouter
object.
import ReactDOM from 'react-dom';
import { BrowserRouter } from "react-router-dom";
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
reportWebVitals();
For React 18:
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from "react-router-dom";
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
reportWebVitals();
Create Services
We’re gonna create two services in src/services folder:
services
auth-header.ts
auth.service.ts (Authentication service)
user.service.ts (Data service)
Before working with these services, we need to install Axios with command:
yarn add axios
or npm install axios
Authentication service
The service uses Axios for HTTP requests and Local Storage for user information & JWT.
It provides following important methods:
login()
: POST {username, password} & saveJWT
to Local Storagelogout()
: removeJWT
from Local Storageregister()
: POST {username, email, password}getCurrentUser()
: get stored user information (including JWT)
import axios from "axios";
const API_URL = "http://localhost:8080/api/auth/";
class AuthService {
login(username: string, password: string) {
return axios
.post(API_URL + "signin", {
username,
password
})
.then(response => {
if (response.data.accessToken) {
localStorage.setItem("user", JSON.stringify(response.data));
}
return response.data;
});
}
logout() {
localStorage.removeItem("user");
}
register(username: string, email: string, password: string) {
return axios.post(API_URL + "signup", {
username,
email,
password
});
}
getCurrentUser() {
const userStr = localStorage.getItem("user");
if (userStr) return JSON.parse(userStr);
return null;
}
}
export default new AuthService();
Data service
We also have methods for retrieving data from server. In the case we access protected resources, the HTTP request needs Authorization header.
Let’s create a helper function called authHeader()
inside auth-header.ts:
export default function authHeader() {
const userStr = localStorage.getItem("user");
let user = null;
if (userStr)
user = JSON.parse(userStr);
if (user && user.accessToken) {
return { Authorization: 'Bearer ' + user.accessToken };
} else {
return { Authorization: '' };
}
}
The code above checks Local Storage for user
item. If there is a logged in user
with accessToken
(JWT), return HTTP Authorization header. Otherwise, return an empty object.
Note: For Node Express back-end, please use x-access-token header like this:
export default function authHeader() {
const userStr = localStorage.getItem("user");
let user = null;
if (userStr)
user = JSON.parse(userStr);
if (user && user.accessToken) {
// return { Authorization: 'Bearer ' + user.accessToken }; // for Spring Boot back-end
return { 'x-access-token': user.accessToken }; // for Node.js Express back-end
} else {
// return { Authorization: '' }; // for Spring Boot back-end
return { 'x-access-token': null }; // for Node Express back-end
}
}
Now we define a service for accessing data in user.service.ts:
import axios from 'axios';
import authHeader from './auth-header';
const API_URL = 'http://localhost:8080/api/test/';
class UserService {
getPublicContent() {
return axios.get(API_URL + 'all');
}
getUserBoard() {
return axios.get(API_URL + 'user', { headers: authHeader() });
}
getModeratorBoard() {
return axios.get(API_URL + 'mod', { headers: authHeader() });
}
getAdminBoard() {
return axios.get(API_URL + 'admin', { headers: authHeader() });
}
}
export default new UserService();
You can see that we add a HTTP header with the help of authHeader()
function when requesting authorized resource.
Create React Typescript Components for Authentication
In src folder, create new folder named components and add several files as following:
components
login.component.tsx
register.component.tsx
profile.component.tsx
React Typescript Form Validation overview
Now we need a library for Form validation, so we’re gonna add formik
and yup
library to our project.
Run the command: yarn add formik yup
Or: npm install formik yup
Import following items:
import { Formik, Field, Form, ErrorMessage } from "formik";
import * as Yup from "yup";
This is how we put them in React Component with 3 important attributes:
initialValues
validationSchema
onSubmit
export default class Login extends Component<Props, State> {
...
validationSchema() {
return Yup.object().shape({
username: [Yup Schema],
password: [Yup Schema],
});
}
handleLogin(formValue: { username: string; password: string }) {
const { username, password } = formValue;
...
}
render() {
const initialValues = {
username: "",
password: "",
};
return (
<Formik
initialValues={initialValues}
validationSchema={this.validationSchema}
onSubmit={this.handleLogin}
>
<Form>
<div>
<label htmlFor="username">Password</label>
<Field name="username" type="text" />
<ErrorMessage name="username" component="div" />
</div>
<div>
<label htmlFor="password">Password</label>
<Field name="password" type="password" />
<ErrorMessage name="password" component="div" />
</div>
<div>
<button type="submit" disabled={loading}>
Login
</button>
</div>
</Form>
</Formik>
);
}
}
More details at:
React Form Validation example with Formik and Yup
Login Page
This page has a Form with username
& password
.
– We’re gonna verify them as required field.
– If the verification is ok, we call AuthService.login()
method, then direct user to Profile page: this.props.history.push("/profile");
, or show message
with response error.
login.component.tsx
– For react-router-dom v5:
import { Component } from "react";
import { RouteComponentProps } from "react-router-dom";
import { Formik, Field, Form, ErrorMessage } from "formik";
import * as Yup from "yup";
import AuthService from "../services/auth.service";
interface RouterProps {
history: string;
}
type Props = RouteComponentProps<RouterProps>;
type State = {
username: string,
password: string,
loading: boolean,
message: string
};
export default class Login extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.handleLogin = this.handleLogin.bind(this);
this.state = {
username: "",
password: "",
loading: false,
message: ""
};
}
validationSchema() {
return Yup.object().shape({
username: Yup.string().required("This field is required!"),
password: Yup.string().required("This field is required!"),
});
}
handleLogin(formValue: { username: string; password: string }) {
const { username, password } = formValue;
this.setState({
message: "",
loading: true
});
AuthService.login(username, password).then(
() => {
this.props.history.push("/profile");
window.location.reload();
},
error => {
const resMessage =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
this.setState({
loading: false,
message: resMessage
});
}
);
}
render() {
const { loading, message } = this.state;
const initialValues = {
username: "",
password: "",
};
return (
<div className="col-md-12">
<div className="card card-container">
<img
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
alt="profile-img"
className="profile-img-card"
/>
<Formik
initialValues={initialValues}
validationSchema={this.validationSchema}
onSubmit={this.handleLogin}
>
<Form>
<div className="form-group">
<label htmlFor="username">Username</label>
<Field name="username" type="text" className="form-control" />
<ErrorMessage
name="username"
component="div"
className="alert alert-danger"
/>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<Field name="password" type="password" className="form-control" />
<ErrorMessage
name="password"
component="div"
className="alert alert-danger"
/>
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary btn-block" disabled={loading}>
{loading && (
<span className="spinner-border spinner-border-sm"></span>
)}
<span>Login</span>
</button>
</div>
{message && (
<div className="form-group">
<div className="alert alert-danger" role="alert">
{message}
</div>
</div>
)}
</Form>
</Formik>
</div>
</div>
);
}
}
– For react-router-dom v6:
- using
Navigate
instead ofRedirect
RouteComponentProps
is not supported anymore
import { Component } from "react";
import { Navigate } from "react-router-dom";
import { Formik, Field, Form, ErrorMessage } from "formik";
import * as Yup from "yup";
import AuthService from "../services/auth.service";
type Props = {};
type State = {
redirect: string | null,
username: string,
password: string,
loading: boolean,
message: string
};
export default class Login extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.handleLogin = this.handleLogin.bind(this);
this.state = {
redirect: null,
username: "",
password: "",
loading: false,
message: ""
};
}
componentDidMount() {
const currentUser = AuthService.getCurrentUser();
if (currentUser) {
this.setState({ redirect: "/profile" });
};
}
componentWillUnmount() {
window.location.reload();
}
validationSchema() {
return Yup.object().shape({
username: Yup.string().required("This field is required!"),
password: Yup.string().required("This field is required!"),
});
}
handleLogin(formValue: { username: string; password: string }) {
const { username, password } = formValue;
this.setState({
message: "",
loading: true
});
AuthService.login(username, password).then(
() => {
this.setState({
redirect: "/profile"
});
},
error => {
...
}
);
}
render() {
if (this.state.redirect) {
return <Navigate to={this.state.redirect} />
}
const { loading, message } = this.state;
const initialValues = {
username: "",
password: "",
};
return (
...
);
}
}
Register Page
This page is similar to Login Page.
For Form Validation, there are some more details:
username
: required, between 3 and 20 charactersemail
: required, email formatpassword
: required, between 6 and 40 characters
We’re gonna call AuthService.register()
method and show response message (successful or error).
register.component.tsx
import { Component } from "react";
import { Formik, Field, Form, ErrorMessage } from "formik";
import * as Yup from "yup";
import AuthService from "../services/auth.service";
type Props = {};
type State = {
username: string,
email: string,
password: string,
successful: boolean,
message: string
};
export default class Register extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.handleRegister = this.handleRegister.bind(this);
this.state = {
username: "",
email: "",
password: "",
successful: false,
message: ""
};
}
validationSchema() {
return Yup.object().shape({
username: Yup.string()
.test(
"len",
"The username must be between 3 and 20 characters.",
(val: any) =>
val &&
val.toString().length >= 3 &&
val.toString().length <= 20
)
.required("This field is required!"),
email: Yup.string()
.email("This is not a valid email.")
.required("This field is required!"),
password: Yup.string()
.test(
"len",
"The password must be between 6 and 40 characters.",
(val: any) =>
val &&
val.toString().length >= 6 &&
val.toString().length <= 40
)
.required("This field is required!"),
});
}
handleRegister(formValue: { username: string; email: string; password: string }) {
const { username, email, password } = formValue;
this.setState({
message: "",
successful: false
});
AuthService.register(
username,
email,
password
).then(
response => {
this.setState({
message: response.data.message,
successful: true
});
},
error => {
const resMessage =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
this.setState({
successful: false,
message: resMessage
});
}
);
}
render() {
const { successful, message } = this.state;
const initialValues = {
username: "",
email: "",
password: "",
};
return (
<div className="col-md-12">
<div className="card card-container">
<img
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
alt="profile-img"
className="profile-img-card"
/>
<Formik
initialValues={initialValues}
validationSchema={this.validationSchema}
onSubmit={this.handleRegister}
>
<Form>
{!successful && (
<div>
<div className="form-group">
<label htmlFor="username"> Username </label>
<Field name="username" type="text" className="form-control" />
<ErrorMessage
name="username"
component="div"
className="alert alert-danger"
/>
</div>
<div className="form-group">
<label htmlFor="email"> Email </label>
<Field name="email" type="email" className="form-control" />
<ErrorMessage
name="email"
component="div"
className="alert alert-danger"
/>
</div>
<div className="form-group">
<label htmlFor="password"> Password </label>
<Field
name="password"
type="password"
className="form-control"
/>
<ErrorMessage
name="password"
component="div"
className="alert alert-danger"
/>
</div>
<div className="form-group">
<button type="submit" className="btn btn-primary btn-block">Sign Up</button>
</div>
</div>
)}
{message && (
<div className="form-group">
<div
className={
successful ? "alert alert-success" : "alert alert-danger"
}
role="alert"
>
{message}
</div>
</div>
)}
</Form>
</Formik>
</div>
</div>
);
}
}
Profile Page
This page gets current User from Local Storage by calling AuthService.getCurrentUser()
method and show user information (with token).
profile.component.tsx
– For react-router-dom v5:
import { Component } from "react";
import { Redirect } from "react-router-dom";
import AuthService from "../services/auth.service";
import IUser from "../types/user.type";
type Props = {};
type State = {
redirect: string | null,
userReady: boolean,
currentUser: IUser & { accessToken: string }
}
export default class Profile extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
redirect: null,
userReady: false,
currentUser: { accessToken: "" }
};
}
componentDidMount() {
const currentUser = AuthService.getCurrentUser();
if (!currentUser) this.setState({ redirect: "/home" });
this.setState({ currentUser: currentUser, userReady: true })
}
render() {
if (this.state.redirect) {
return <Redirect to={this.state.redirect} />
}
const { currentUser } = this.state;
return (
<div className="container">
{(this.state.userReady) ?
<div>
<header className="jumbotron">
<h3>
<strong>{currentUser.username}</strong> Profile
</h3>
</header>
<p>
<strong>Token:</strong>{" "}
{currentUser.accessToken.substring(0, 20)} ...{" "}
{currentUser.accessToken.substr(currentUser.accessToken.length - 20)}
</p>
<p>
<strong>Id:</strong>{" "}
{currentUser.id}
</p>
<p>
<strong>Email:</strong>{" "}
{currentUser.email}
</p>
<strong>Authorities:</strong>
<ul>
{currentUser.roles &&
currentUser.roles.map((role, index) => <li key={index}>{role}</li>)}
</ul>
</div> : null}
</div>
);
}
}
– For react-router-dom v6 (using Navigate
instead of Redirect
):
import { Component } from "react";
import { Navigate } from "react-router-dom";
//...
export default class Profile extends Component<Props, State> {
...
render() {
if (this.state.redirect) {
return <Navigate to={this.state.redirect} />
}
const { currentUser } = this.state;
return (
...
);
}
}
Create React Typescript Components for accessing Resources
These components will use UserService
to request data from API.
components
home.component.tsx
board-user.component.tsx
board-moderator.component.tsx
board-admin.component.tsx
Home Page
This is a public page that shows public content. People don’t need to log in to view this page.
home.component.tsx
import { Component } from "react";
import UserService from "../services/user.service";
type Props = {};
type State = {
content: string;
}
export default class Home extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
content: ""
};
}
componentDidMount() {
UserService.getPublicContent().then(
response => {
this.setState({
content: response.data
});
},
error => {
this.setState({
content:
(error.response && error.response.data) ||
error.message ||
error.toString()
});
}
);
}
render() {
return (
<div className="container">
<header className="jumbotron">
<h3>{this.state.content}</h3>
</header>
</div>
);
}
}
Role-based Pages
We’re gonna have 3 pages for accessing protected data:
- BoardUser page calls
UserService.getUserBoard()
- BoardModerator page calls
UserService.getModeratorBoard()
- BoardAdmin page calls
UserService.getAdminBoard()
I will show you User Page for example, other Pages are similar to this Page.
board-user.component.tsx
import { Component } from "react";
import UserService from "../services/user.service";
type Props = {};
type State = {
content: string;
}
export default class BoardUser extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
content: ""
};
}
componentDidMount() {
UserService.getUserBoard().then(
response => {
this.setState({
content: response.data
});
},
error => {
this.setState({
content:
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString()
});
}
);
}
render() {
return (
<div className="container">
<header className="jumbotron">
<h3>{this.state.content}</h3>
</header>
</div>
);
}
}
Now we add a navigation bar in App
component. This is the root container for our application.
The navbar dynamically changes by login status and current User’s roles.
- Home: always
- Login & Sign Up: if user hasn’t signed in yet
- User:
AuthService.getCurrentUser()
returns a value - Board Moderator: roles includes
ROLE_MODERATOR
- Board Admin: roles includes
ROLE_ADMIN
src/App.tsx
– For react-router-dom v5:
import { Component } from "react";
import { Switch, Route, Link } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
import "./App.css";
import AuthService from "./services/auth.service";
import IUser from './types/user.type';
import Login from "./components/login.component";
import Register from "./components/register.component";
import Home from "./components/home.component";
import Profile from "./components/profile.component";
import BoardUser from "./components/board-user.component";
import BoardModerator from "./components/board-moderator.component";
import BoardAdmin from "./components/board-admin.component";
import EventBus from "./common/EventBus";
type Props = {};
type State = {
showModeratorBoard: boolean,
showAdminBoard: boolean,
currentUser: IUser | undefined
}
class App extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.logOut = this.logOut.bind(this);
this.state = {
showModeratorBoard: false,
showAdminBoard: false,
currentUser: undefined,
};
}
componentDidMount() {
const user = AuthService.getCurrentUser();
if (user) {
this.setState({
currentUser: user,
showModeratorBoard: user.roles.includes("ROLE_MODERATOR"),
showAdminBoard: user.roles.includes("ROLE_ADMIN"),
});
}
EventBus.on("logout", this.logOut);
}
componentWillUnmount() {
EventBus.remove("logout", this.logOut);
}
logOut() {
AuthService.logout();
this.setState({
showModeratorBoard: false,
showAdminBoard: false,
currentUser: undefined,
});
}
render() {
const { currentUser, showModeratorBoard, showAdminBoard } = this.state;
return (
<div>
<nav className="navbar navbar-expand navbar-dark bg-dark">
<Link to={"/"} className="navbar-brand">
bezKoder
</Link>
<div className="navbar-nav mr-auto">
<li className="nav-item">
<Link to={"/home"} className="nav-link">
Home
</Link>
</li>
{showModeratorBoard && (
<li className="nav-item">
<Link to={"/mod"} className="nav-link">
Moderator Board
</Link>
</li>
)}
{showAdminBoard && (
<li className="nav-item">
<Link to={"/admin"} className="nav-link">
Admin Board
</Link>
</li>
)}
{currentUser && (
<li className="nav-item">
<Link to={"/user"} className="nav-link">
User
</Link>
</li>
)}
</div>
{currentUser ? (
<div className="navbar-nav ml-auto">
<li className="nav-item">
<Link to={"/profile"} className="nav-link">
{currentUser.username}
</Link>
</li>
<li className="nav-item">
<a href="/login" className="nav-link" onClick={this.logOut}>
LogOut
</a>
</li>
</div>
) : (
<div className="navbar-nav ml-auto">
<li className="nav-item">
<Link to={"/login"} className="nav-link">
Login
</Link>
</li>
<li className="nav-item">
<Link to={"/register"} className="nav-link">
Sign Up
</Link>
</li>
</div>
)}
</nav>
<div className="container mt-3">
<Switch>
<Route exact path={["/", "/home"]} component={Home} />
<Route exact path="/login" component={Login} />
<Route exact path="/register" component={Register} />
<Route exact path="/profile" component={Profile} />
<Route path="/user" component={BoardUser} />
<Route path="/mod" component={BoardModerator} />
<Route path="/admin" component={BoardAdmin} />
</Switch>
</div>
{ /*<AuthVerify logOut={this.logOut}/> */}
</div>
);
}
}
export default App;
– For react-router-dom v6 (using Routes
instead of Switch
):
import { Component } from "react";
import { Routes, Route, Link } from "react-router-dom";
// ...
class App extends Component<Props, State> {
...
render() {
const { currentUser, showModeratorBoard, showAdminBoard } = this.state;
return (
<div>
...
<div className="container mt-3">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/home" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
<Route path="/profile" element={<Profile />} />
<Route path="/user" element={<BoardUser />} />
<Route path="/mod" element={<BoardModerator />} />
<Route path="/admin" element={<BoardAdmin />} />
</Routes>
</div>
</div>
);
}
}
export default App;
You can simplify import statement with:
Absolute Import in React
Logout when the Token is expired
There are two ways. For more details, please visit:
React – How to Logout when JWT Token is expired
Add CSS style for React Typescript Components
Open src/App.css and write some CSS code as following:
label {
display: block;
margin-top: 10px;
}
.card-container.card {
max-width: 350px !important;
padding: 40px 40px;
}
.card {
background-color: #f7f7f7;
padding: 20px 25px 30px;
margin: 0 auto 25px;
margin-top: 50px;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
border-radius: 2px;
-moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3);
}
.profile-img-card {
width: 96px;
height: 96px;
margin: 0 auto 10px;
display: block;
-moz-border-radius: 50%;
-webkit-border-radius: 50%;
border-radius: 50%;
}
Configure Port for React Typescript Login with Web API
Because most of HTTP Server use CORS configuration that accepts resource sharing restricted to some sites or ports, so we also need to configure port for our App.
In project folder, create .env file with following content:
PORT=8081
Now we’ve set our app running at port 8081
. You will need to do this work if you use one of following Servers:
- Spring Boot JWT Authentication with Spring Security MySQL
- Spring Boot JWT Authentication with Spring Security, PostgreSQL
- Spring Boot JWT Authentication with Spring Security, MongoDB
- Node JWT Authentication & Authorization with MySQL
- Node JWT Authentication & Authorization with MongoDB
- Node JWT Authentication & Authorization with PostgreSQL
Conclusion
Congratulation!
Today we’ve done so many interesting things. I hope you understand the overall layers of our React Typescript Login and Registration Application (without Redux) using LocalStorage, React Router, Axios, Bootstrap. Now you can apply it in your project at ease.
You should continue to check if Token is expired and logout:
React – How to Logout when JWT Token is expired
Or add refresh token:
React Refresh Token with JWT and Axios Interceptors
If you want to use React Hooks for this example, you can find the implementation at:
React Typescript Authentication example with Hooks
Or using Redux for state management:
React Redux: JWT Authentication & Authorization example
Happy learning, see you again!
Further Reading
- React Router Guide
- React Components
- Formik Overview
- https://github.com/jquense/yup
- In-depth Introduction to JWT-JSON Web Token
– React Typescript File Upload example
Fullstack CRUD:
– React + Spring Boot + MySQL
– React + Spring Boot + PostgreSQL
– React + Spring Boot + MongoDB
– React + Node Express + MySQL
– React + Node Express + PostgreSQL
– React + Node Express + MongoDB
– React + Django
Serverless with Firebase:
– React Typescript Firebase CRUD with Realtime Database
– React Typescript Firestore CRUD example with Cloud Firestore
Source Code
You can find the complete source code for this example on Github.
Using Typescript with React Hooks instead:
React Typescript Authentication example with Hooks
Hello, thanks for this nice tuto, is it a choice to don’t use the react-router-dom v6 ?
I found that some codes in tutorial area different from files in repo :). For example here there is no word about ‘EventBus.dispatch(“logout”);’ but it can be found in GIT files :).
Hi, thanks for your comment.
You can find the code in the tutorial that I mentioned in Logout_when_token_expired section.
Yes, 2 files are missing
– ‘src/types/user.type’,
– ‘./common/EventBus’
Hey. I love it, thank you so much :))). but The IUser from “../types/user.type” you don’t include on post 🙁
You can find working code in GIT repository: https://github.com/bezkoder/react-typescript-login-example
Hi, Could you create similar tutorial (TS) but with hooks?
Hi, here you are:
React Typescript Authentication example with Hooks
Your React tutorial provided me with useful practice to work on. Thanks!