Now that we have the database and backend set up, let’s create the UI. We’re using the MERN stack, where “R” stands for React. We’ll be using Next.js, a React framework that extends React’s capabilities.
Setting Up Next.js
To create a Next.js application, run the following command:npx create-next-app@latest
You’ll be prompted with several configuration choices:
- Ok to proceed? → y
- What is your project named? → ui
- Would you like to use TypeScript? → Yes
- Would you like to use ESLint? → Yes
- Would you like to use Tailwind CSS? → Yes
- Would you like your code inside a src/ directory? → No
- Would you like to use App Router? → Yes
- Would you like to use Turbopack for next dev? → No
- Would you like to customize the import alias (@/* by default)? → No
Start the Development Server
Navigate into the ui folder and run the app:
cd ui
npm run dev
Install Axios
Next, install Axios to handle API requests:npm install axios
Clean Up page.tsx
Delete everything inside page.tsx, so it looks like this:
export default function Home()
return (
<div>
<h1>Things</h1>
</div>
);
Now, let’s set next.js to ‘use client’, add imports, and create our interfaces. In Next.js 13, components are server components by default. Server components are rendered on the server and sent as static HTML to the client. They are great for performance and SEO but cannot use client-side features such as react hooks.
'use client'
import useEffect, useState from 'react';
import axios from 'axios';
interface Thing
_id: string;
title: string;
description: string;
Now, add the following methods to the default Home function.
const [things, setThings] = useState<Thing[]>([]);
const [selectedThing, setSelectedThing] = useState<Thing | null>(null);
// Base API endpoint
const API_BASE_URL = 'http://localhost:5000/things';
useEffect(() =>
getTasks();
, []);
// Handles radio button click and populates the form with the selected thing's data
const handleRadioClick = (thing: Thing) =>
setSelectedThing(thing);
populateForm(thing);
;
// Populates the form fields with the data of the selected thing
const populateForm = (thing: Thing) =>
const form = document.querySelector('form');
if (form)
(form.querySelector('input[name="title"]') as HTMLInputElement).value = thing.title;
(form.querySelector('input[name="description"]') as HTMLInputElement).value = thing.description;
;
// Clears the selected thing and resets the form and radio button selection
const clearSelectedThing = () =>
setSelectedThing(null);
clearForm();
clearRadioSelection();
;
// Clears the form fields
const clearForm = () =>
const form = document.querySelector('form');
if (form)
(form.querySelector('input[name="title"]') as HTMLInputElement).value="";
(form.querySelector('input[name="description"]') as HTMLInputElement).value="";
;
// Clears the radio button selection
const clearRadioSelection = () =>
const radios = document.querySelectorAll('input[type="radio"]');
radios.forEach(radio => (radio as HTMLInputElement).checked = false);
;
// Fetches the list of tasks from the server
const getTasks = async () =>
try
const res = await axios.get(API_BASE_URL);
setThings(res.data);
catch (error)
console.error('Error fetching tasks:', error);
;
// Adds or edits a task based on the form data
const addEditTask = async (event: React.FormEvent) =>
event.preventDefault();
const form = event.target as HTMLFormElement;
const formData = new FormData(form);
const title = formData.get('title') as string;
const description = formData.get('description') as string;
try
if (selectedThing)
const res = await axios.put(`$API_BASE_URL/$selectedThing._id`, title, description );
setThings(things.map(thing => (thing._id === selectedThing._id ? res.data : thing)));
else
const res = await axios.post(API_BASE_URL, title, description );
setThings([...things, res.data]);
clearSelectedThing();
catch (error)
console.error('Error adding/editing task:', error);
;
// Deletes the selected task
const deleteSelectedThing = async () =>
if (selectedThing)
try
await axios.delete(`$API_BASE_URL/$selectedThing._id`);
setThings(things.filter(thing => thing._id !== selectedThing._id));
clearSelectedThing();
catch (error)
console.error('Error deleting task:', error);
;
Finally, replace all the html being returned with the following code.
<div>
<h1>Things</h1>
<h2>All Tasks</h2>
<ul>
things.map((thing) => (
<li key=thing._id>
<input
type="radio"
id=thing._id
name="thing"
onClick=() => handleRadioClick(thing)
/>
<label htmlFor=thing._id>
thing.title - thing.description
</label>
</li>
))
</ul>
<h2>selectedThing === null ? 'Add' : 'Edit' Task</h2>
<form onSubmit=addEditTask>
Name: <input type="text" name="title" /><br />
Description: <input type="text" name="description" /><br />
<button type="submit">selectedThing === null ? 'Add Task' : 'Edit Task'</button>
</form>
<button onClick=clearSelectedThing>Clear Selected Thing</button>
<button onClick=deleteSelectedThing>Delete Selected Thing</button>
</div>
Bam! There you go! You now have full CRUD applications.