Building a React App With Server-Side Swift
In this tutorial, you’ll learn to create a dynamic web application with React.js and integrate it with a Vapor server-side application. By Mattia Fochesato.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Building a React App With Server-Side Swift
25 mins
- Getting Started
- Installing Node.js and npm
- Creating the React App
- Adding Bootstrap Theme and React Router
- Understanding React App Structure
- Creating the BookList Component
- Routing With React Router
- Creating a Responsive Page With Bootstrap
- Creating a Table to Display Books
- Fetching Data From the Vapor app
- Creating a Page to Add New Books
- Creating a Form
- Handling Form Data
- Building the React App for Production
- Serving the App With Vapor
- Where to Go From Here
React.js is the most popular JavaScript library used for building dynamic web apps. It is straightforward to learn, lets you quickly create complex apps, and provides good performance.
In this tutorial, you’ll see how to create MyBrary, a simple web app that manages your book library.
You’ll learn how to:
- Develop a simple React app.
- Make different pages with React Router.
- Make requests to the Vapor server from React.
- Serve the React app from Vapor.
Getting Started
Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial.
The starter project includes a backend application with a simple REST API that allows you to save and browse a collection of books. Open a Terminal window and navigate to the starter/backend-vapor folder. Now, run the following:
swift run
The first time you run this command, it can take a while because it has to fetch all the dependencies and compile all the code. However, after the first run, it should be fast to compile the changes you’ve made to the app. When the command finishes, you’ll see the following message:
[ NOTICE ] Server starting on http://127.0.0.1:8080
Now open a new Terminal window and enter the following command:
curl http://127.0.0.1:8080/books
This will return a JSON-formatted list of books, like this:
[
{
"id": "59AB6499-EB0A-4AAA-AA91-BDF0DAA4E3F1",
"author": "Logan Wright, Tim Condon and Tanner Nelson",
"title": "Server-Side Swift with Vapor"
},
{
"id": "6F7B5287-6F8D-4324-8307-63A337BFF0A4",
"author": "Scott Grosch",
"title": "Concurrency by Tutorials"
}
]
The backend part is ready. Now, it’s time to work on the frontend!
Before starting the tutorial, install Node.js if you don’t already have it on your machine. This is because React apps need Node.js to run the development server, pack the build version and manage dependencies.
Installing Node.js and npm
To create and run your React app, you have to install Node.js and its package manager, npm. The procedure is straightforward using nvm, a tool that helps you manage your Node installation. To install nvm, open a new Terminal window and run this command:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
After that, close and reopen the terminal, and let nvm install Node with this command:
nvm install node
This will install the latest version of Node. At this point, grab a cup of coffee and wait for the completion. It can take a while.
Once the installation has completed, make the installed Node version the default one, using the alias
command:
nvm alias default v18.4.0
v18.4.0
is the most recent version of Node.
Now, you are all set. It’s time to build a React app!
Creating the React App
You’ll use the create-react-app
CLI to set up your React project with one command.
In a new Terminal window, type this command to create your new app:
npx create-react-app frontend
npx
is a package runner: It automatically downloads and updates the package before executing it. With npx
, you can run create-react-app
without worrying about installing it or keeping it updated. frontend
refers to the project’s name.
After you’ve created the project, use this command to navigate to the new project folder.
cd frontend
You’re now in the working directory of your new project. From here, you can add dependencies and run your app.
Adding Bootstrap Theme and React Router
React is a framework that only handles drawing the interface on the browser. Therefore, if you want to add more features to React, you have to install additional packages.
Still in the frontend folder, run the following command:
npm install react-bootstrap bootstrap react-router-dom
Using npm install
, you’re telling the Node Package Manager to install the specified packages in the current working directory:
-
react-bootstrap
is the React version of Bootstrap, a powerful toolkit for building responsive websites. - The
react-bootstrap
library needsbootstrap
to use Bootstrap in your app. -
react-router-dom
is a library that handles the routing client-side. Using React Router, you don’t need to use any fancy hosting platform for your app.
Now, open src/index.js, and add this line right before the const root
declaration:
// Allows you to use Bootstrap on every page of the app
import 'bootstrap/dist/css/bootstrap.min.css';
This will include Bootstrap on every page of your app.
It’s now time to start the development server for your first React app. Run this command from the frontend folder:
npm start
After a few seconds, your browser will open a new page showing you React’s starting page, available at the URL http://localhost:3000.
Note that you only have to run this command once. After that, every time you edit a file, React automatically updates the page on your browser.
Now, the React app is running — in development mode — and so is the Vapor app. It’s time to hook them up, but first you’ll take a look at the project structure.
Understanding React App Structure
Before you dive into the creation of the app, you should know the project’s structure:
- The node_modules folder contains all of your app’s dependencies. Don’t touch this folder because npm automatically generates and updates it.
- Inside the public folder, you can put any asset your app should access, like images, stylesheet files, etc.
- The src folder is your working directory; you’ll create your components inside this folder.
- npm uses the package-lock.json file to track all the dependencies you’ve installed. You should not edit this file.
- To change your app’s dependencies, you edit the package.json file.
Creating the BookList Component
You can compose a UI with React by combining different components. A component is a JavaScript function — or a class — that receives an input and gives a React element as output.
You’ll start with a component to list the books present in your library.
Within the src folder, create a new file called BookList.js, and add the following code:
import { useState, useEffect } from "react";
import { Container, Row, Stack, Button, Table } from "react-bootstrap";
import { NavLink } from "react-router-dom";
// 1
function BookList() {
// TODO: Define variables and functions needed
return (
<>
{/* 2 */}
{/* TODO: Replace with React components */}
</>
);
}
// 3
export default BookList;
Here’s what this code does:
- Defines the component called
BookList
. - Lets you compose your interface using React components.
- Exports the component so other components can use it. For example, you can use this component inside the index.js file.
Your first custom component is now ready, but to use it, you’ll need to set up the routing of the React app.
Routing With React Router
If you want your app to have many pages, you must implement a Router component, and then you’ll be able to specify a path for each component.
There are many types of router:
- HashRouter allows you to create a single-page app. The page change is instantaneous since the browser isn’t changing the page. It’s all handled by the router on the client side.
- BrowserRouter is the standard routing method. Each page has its path. Changing a page is slower than the HashRouter, and the state management can be a little trickier.
- React Router supports other types of Routers as well. You can find all of them inside their documentation.
In this tutorial you’ll use HashRouter since it’s easier to implement. Open index.js and replace the < App />
tag with this:
{/* 1 */}
<HashRouter>
{/* 2 */}
<Routes>
{/* 3 */}
<Route exact path="/" element={<BookList />} />
{/* TODO: Define any additional Route */}
</Routes>
</HashRouter>
Going through this step by step:
- The
HashRouter
tag allows you to create a single-page web app with client-side routing. - The
Routes
tag component will contain a list ofRoute
s, which define the paths available in the app. - This defines the root path
/
, which will soon show theBookList
component.
Don’t forget to import the components at the beginning of the index.js file:
// Allows you to use ReactRoute components inside this file
import { HashRouter, Routes, Route } from "react-router-dom";
import BookList from "./BookList";
Now, save the file, and React will update the preview with the new index. The page at http://localhost:3000 is now empty since you still haven’t put any components inside it. That’s your next step.
Creating a Responsive Page With Bootstrap
Bootstrap uses a grid system to align content on the webpage. Thanks to containers, rows and columns, you can create responsive web apps with a few lines of code without worrying too much about the layout.
To start creating the user interface using Bootstrap, open BookList.js and replace {/* TODO: Replace with React components */}
with the following:
{/* 1 */}
<Container>
{/* 2 */}
<Row>
{/* 3 */}
<Stack direction="horizontal" className="mt-3 mb-3">
<h2>Book List</h2>
{/* TODO: Add Button to navigate to the "New Book" page */}
</Stack>
{/* TODO: Add the Table component */}
</Row>
</Container>
Here’s what each step does:
- You use the
Container
tag to center the content of your component horizontally. - Since Bootstrap uses a grid system, you use
Row
to represent a row, which will be an entry that displays a book. - The
Stack
tag creates a flexible container, which will have ahorizontal
direction and additional attributes such asmt-3
for the top margin andmb-3
for the bottom margin.
Now the page at http://localhost:3000 will look like this.
At this point, the only thing missing is the table that displays the list of books in the library.
Creating a Table to Display Books
Before implementing the table, you need to define a variable to hold the data that React will fetch from the Vapor app.
Since the variable you’ll define will be used to draw the interface, React needs to know when the value of the variable changes. So you have to use the useState()
hook to define new variables that can change the components’ layout.
In BookList.js, replace // TODO: Define variables and functions needed
with:
// Define all the variables needed for the book list
const [booksData, setBooksData] = useState(null);
// TODO: Add function to load books
useState()
needs an initial value and it returns the current state and a function.
You’ve created a state variable called booksData
with the starting value of null
. You can change the value of booksData
by calling setBooksData()
, passing the new value as a parameter.
Still in BookList.js, replace {/* TODO: Add the Table component */}
with the following code:
{/* Bootstrap Table */}
<Table bordered hover>
{/* Table Header */}
<thead>
<tr>
<th>Book Name</th>
<th>Author</th>
</tr>
</thead>
{/* Table Body */}
<tbody>
{/* 1 */}
{booksData &&
booksData.map(({ id, title, author }) => (
<>
{/* 2 */}
<tr key={id}>
{/* 3 */}
<td>{title}</td>
<td>{author}</td>
</tr>
</>
))}
</tbody>
</Table>
This will construct the table that shows the list of books. Here’s what’s happening:
- Check whether
booksData
isnull
. Then, render the data as a table row. - Set the
key
of the row. React will use it to calculate diffs and optimize the performance of redrawing the UI. - Show the book’s
title
andauthor
as a table cell.
Now, the web page of the React app looks like this.
What’s missing? Actual books! :] Your next step is fetching them from the Vapor app.
Fetching Data From the Vapor app
In BookList.js, replace // TODO: Add function to load books
with:
// Function that will load the books from the back end
function loadBooks() {
// 1
setBooksData(null);
// 2
fetch("http://localhost:8080/books/")
// 3
.then((response) => response.json())
// 4
.then((json) => setBooksData(json));
}
// 5
useEffect(() => {
loadBooks();
}, []);
This fetches and stores the books in the booksData
variable. A lot’s going on here. This code:
- Clears the
booksData
variable to remove previously loaded books. - Fetches the books from the backend using the
fetch()
function. - Converts the response from a JSON string to a JavaScript object.
- Saves the object in the
booksData
variable usingsetBookData(json)
. - Calls
loadBooks()
via theuseEffect()
hook.
useEffect()
accepts a function and an array of dependencies as an argument. If any dependencies’ values change, the hook triggers the function. React also executes the function one time on the first render of the component.
Here, there are no dependencies to track since you only need to execute the loadBooks()
function when the component is loaded.
It’s crucial to call loadBooks()
from the useEffect()
hook, because loadBooks()
fetches data and causes the content on the view to change.
Check the page at http://localhost:3000 again, and you’ll see the list of books displayed.
Congrats on your first achievement! Now, what about adding new books to the list? For that, you’ll need a new component and a new page.
Creating a Page to Add New Books
You’ll start with a new component that allows users to input the book’s details and send them to the Vapor backend.
In src, create a new file called NewBook.js, and add the following code:
import { useState } from "react";
import { Button, Container, Form, Row, Stack } from "react-bootstrap";
import { NavLink, useNavigate } from "react-router-dom";
function NewBook() {
// 1
const [title, setTitle] = useState("");
const [author, setAuthor] = useState("");
// TODO: Define variables that are needed
// 2
const onSubmit = (e) => {
// TODO: Implement this function
};
return (
<>
{/* Bootstrap container */}
<Container>
<Row>
{/* 3 */}
<Stack direction="horizontal" className="mt-3 mb-3">
<h2>Add a new book</h2>
<Button className="ms-auto" as={NavLink} to="/">Show all books</Button>
</Stack>
{/* 4 */}
<Form onSubmit={onSubmit}>
{/* TODO: Add components to accept user input */}
</Form>
</Row>
</Container>
</>
);
}
export default NewBook;
In this new component:
- The
title
andauthor
variables hold the value of the form input elements. -
onSubmit()
handles theForm
data and sends it to the back end. - A
Stack
tag defines the name of the page and aButton
tag navigates to the Book list page. - The
Form
component accepts theonSubmit()
function. When the user hits the Submit button, the browser calls this function.
You now have the new component, but you still can’t access it. As you did with the BookList
component, you have to add a new route and specify a path.
Open src/index.js and replace {/* TODO: Define any additional Route */}
with the following:
{/* Define the /newbook path that will show the NewBook component */}
<Route path="/newbook" element={<NewBook />} />
And don’t forget to import NewBook
at the beginning of the index.js file:
import NewBook from "./NewBook";
Now, visit http://localhost:3000/#/newbook in your browser to verify the page exists.
It would be great to have a way to access this new page without knowing the path. You can use a Button to take the user to the new page.
Open BookList.js and replace {/* TODO: Add Button to navigate to the "New Book" page */}
with this snippet:
{/* Button used to navigate to the "/newbook" page */}
<Button className="ms-auto" as={NavLink} to="/newbook">Add new book</Button>
By adding this button at the top of the Book List page, you’ll be able to access the new page easily.
You might also notice that the new page loads instantaneously. This is because HashRouter handles all the routing on the client side.
Creating a Form
You’ll ask the user for the information about the new book using the Form
. Bootstrap has beautiful forms that just need a bit more code.
In NewBook.js, replace {/* TODO: Add components to accept user input */}
with the following:
{/* 1 */}
<Form.Group className="mb-3">
{/* 2 */}
<Form.Label>Book</Form.Label>
{/* 3 */}
<Form.Control
type="text"
required
placeholder="Name of the book"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Author</Form.Label>
<Form.Control
type="text"
required
placeholder="Name of the author"
value={author}
onChange={(e) => setAuthor(e.target.value)}
/>
</Form.Group>
{/* 4 */}
<Button variant="primary" type="submit">
Add
</Button>
Here’s the structure of this Form
:
- A
Form.Group
is just a container for a single input value. WithclassName="mb-3"
, you specify that this component has a margin-bottom. - The
Form.Label
component shows a title on top of the input label. - The
Form.Control
is the text field used to input the book’s title. When the input’s value changes, the app updates thetitle
variable thanks toonChange()
that callssetTitle()
, passing the value of the label. - To submit the form, you’ll use the Bootstrap Button, but you have to specify that it’s a
submit
button. You alter the appearance of the button by changing the value ofvariant
.
Now, visit http://localhost:3000/#/newbook, and you’ll see a form like this.
It’s time to save the data that users will enter in the form.
Handling Form Data
Notice that hitting the Add button doesn’t add the book to the list because you still haven’t told React what to do with the form data.
Before completing the implementation of onSubmit()
, you need to declare a new variable. Still in src/NewBook.js, replace // TODO: Define variables that are needed
with:
const navigate = useNavigate();
By using navigate
, you can redirect the user to another page by calling navigate("path")
. React Router provides this function, and you can use it with every Router you choose.
Now, add this code inside onSubmit()
to handle the form submit:
// 1
e.preventDefault();
// 2
fetch("http://localhost:8080/books/", {
method: "POST",
// 3
body: JSON.stringify({ title, author }),
headers: { "Content-Type": "application/json" },
})
// 4
.then(() => {
// Navigate to the book list (index) using React Router
navigate("/");
});
It should be familiar, but take a look anyway:
- When the user submits the form,
e.preventDefault()
prevents the browser from redirecting the user to another page. - Make a
POST
request to the API with the new book’s data. - Encode the new book’s data as a JSON object and place it in the request’s
body
. - With
then()
, you specify what happens after the request is sent to the server.
Now, visit http://localhost:3000/#/newbook, and add a new book to the list using the form you just created.
After the book is created, you’re redirected to the list page, which includes also the new entry.
Congrats! You’re almost done. It’s time to package your React app and ship it to customers.
Building the React App for Production
Now that your app is complete, you’re ready to deploy the production version.
You shouldn’t deploy your development build since it can be slow. When you build your app for production, React removes all the debugging tools and minimizes the source files. This way, your app loads faster.
Quit the running npm start
command and run this command from the frontend folder:
npm run build
This will run the build
script, which generates a build folder containing all the necessary assets to run your app. Then, you can host it on a web server or with Vapor.
Serving the App With Vapor
Why should you pay for a hosting service while using Vapor for your backend? It’s very straightforward to host your React app on your Vapor server.
React handles the navigation with React Router. You only need to serve the index.html file and all the other assets. React Router reads the path from the browser and shows the appropriate page.
Copy the contents of the build folder inside the Public folder of the Vapor app.
You’ll have to add a small piece of code to your Vapor server before you can test your app.
Open backend-vapor/Sources/App/routes.swift and add this function at the bottom of the file:
/* Function that returns the content of the `index.html` file from the Public folder */
func serveIndex(_ req: Request, _ app: Application) -> Response {
return req.fileio.streamFile(at: "\(app.directory.publicDirectory)/index.html")
}
Thanks to the serveIndex()
function, you can return the content of the index.html file present in the Public folder. You need this function to serve the index.html file for the / path.
Inside the routes(_ app: Application)
function of the routes.swift file, add these lines:
// 1
app.get { req in
return serveIndex(req, app)
}
// 2
app.get("*") { req in
return serveIndex(req, app)
}
Here’s what it does:
- Serves the index.html file when Vapor receives a GET request for the / path.
- This piece of code is only needed if you’re using the BrowserRouter in your React app. With this route, you tell Vapor that if there’s a GET request at the base path /, and if the Public folder has no file of that name, it returns the contents of the index.html file.
If you’re running Vapor from Xcode, make sure to set the working directory of your project.
Now, run your Vapor server and navigate to http://127.0.0.1:8080 to test your app.
Where to Go From Here
You can download the complete project using the Download Materials button at the top or bottom of this tutorial.
In this tutorial, you’ve seen the basics of a React app, but if you want to discover React’s more advanced features, you should check its documentation.
If you want to learn more about React Router, check out the documentation or take a deep dive into it.
The React Bootstrap library gives you a lot of prebuilt components. Check out the documentation if you want to learn more.
Consider looking at Next.js, a React Framework that uses React for building the UI and allows it to handle routing, data fetching and more in a more straightforward way. With Next.js, you can also render the page server-side or create an API endpoint!
To learn more about the backend code in this tutorial, check out Server-Side Swift with Vapor.
Thank you for taking the time to read this tutorial! If you have any questions or comments, please join the forum discussion below.
All videos. All books.
One low price.
A Kodeco subscription is the best way to learn and master mobile development — plans start at just $19.99/month! Learn iOS, Swift, Android, Kotlin, Flutter and Dart development and unlock our massive catalog of 50+ books and 4,000+ videos.
Learn more