- Sebastian's Blog
- Posts
- Building a Dynamic Frontend with Next.js 14
Building a Dynamic Frontend with Next.js 14
Transforming Data and Integrating APIs with Next.js 14

In the previous part, I showed how I built a scalable web crawler and set up an API to serve the data. Now, it's time to integrate this data into a front-end application using Next.js 14 and DaisyUI.
Shaping Data and Consuming the API with Next.js 14
Setting Up Next.js 14
To get started, we'll install the latest version of Next.js. Follow these steps to set up your project:
Initialize a New Next.js Project:
npx create-next-app@latest
Install Dependencies:
In addition to Next.js, we'll be using Nivo charts for data visualization and DaisyUI for UI components. Install these packages by running:
npm add @nivo/pie npm add @nivo/bar npm i -D daisyui@latest
DaisyUI is a Tailwind CSS component library that provides pre-styled components to speed up development. You can find more details here DaisyUI
Setting Up the Layout
First, let's create a layout that includes a navbar and a sidebar. The layout will provide a consistent structure for all the pages in our application.
// layout.js
import Sidebar from "./components/sidebar/Sidebar";
import Navbar from "./navbar/Navbar";
export const metadata = {
title: "Dashboard",
description: "A Dashboard to shape extracted data",
};
export default function RootLayout({ children }) {
return (
<html data-theme="halloween" lang="en">
<body className={inter.className}>
<Navbar />
<Sidebar>{children}</Sidebar>
</body>
</html>
);
}
In this code, we set up a global layout with a Navbar and Sidebar. We also apply a "halloween" theme from DaisyUI.
The <Sidebar/> component contains navigation links to different parts of the application.
// Sidebar.jsx
import Link from "next/link";
export default function Sidebar({ children }) {
return (
<div className="drawer lg:drawer-open">
<input id="my-drawer-2" type="checkbox" className="drawer-toggle" />
<div className="drawer-content flex flex-col p-10">
{children}
<label
htmlFor="my-drawer-2"
className="btn btn-ghost drawer-button justify-start absolute top-0 left-0 lg:hidden"
>
☰
</label>
</div>
<div className="drawer-side">
<label
htmlFor="my-drawer-2"
aria-label="close sidebar"
className="drawer-overlay"
></label>
<ul className="menu bg-base-200 text-base-content min-h-full w-80 p-4">
<li>
<Link href={'/jobs'}>Jobs</Link>
</li>
<li>
<Link href={'/charts'}>Charts</Link>
</li>
</ul>
</div>
</div>
);
}
The sidebar is a responsive drawer that contains links to the "Jobs" and "Charts" pages. I used the drawer component from DaisyUI.
Displaying Job Data
Next, I will set up a page to display job data fetched from our API.
Jobs page
The Jobs page component uses a server-side component to fetch and display the job data.
// jobs/page.jsx
import JobsServer from "./jobs.server";
export default function Jobs() {
return (
<div className="overflow-x-auto">
<JobsServer/>
</div>
);
}
Job Server component
In the jobs.server component, I fetch the job data from the API and pass it to the <JobsTable/> component. This component is a Table component from DaisyUI.
//jobs.server.jsx
import JobsTable from "../components/table/JobsTable";
export default async function JobsServer() {
const response = await fetch("<http://localhost:3000/jobs>");
const data = await response.json();
return <JobsTable jobs={data} />;
}
So this is the result: We are showing relevant information of the scraped data from Seek.

Visualizing Data with Nivo.
To make the data more engaging and useful, Im using the Nivo Pie and Bar chart for visualizations.
Nivo provides a variety of chart types, including pie, bar, line, and more. For this project, I'll focus on using the Pie and Bar chart to display scraped data. I will use as example the Nivo pie chart.
PieChart
The <PieChart/> component renders a pie chart using data fetched from the API.
So the code looks like this
//**PieChart.jsx**
import { ResponsivePieCanvas } from "@nivo/pie";
const CustomTooltip = ({ datum }) => (
<div style={{ color: 'white', backgroundColor: 'black', padding: '5px', borderRadius: '3px' }}>
<strong>{datum.id}</strong>: {datum.value}
</div>
);
const PieChart = ({ data }) => (
<ResponsivePieCanvas
data={data}
margin={{ top: 40, right: 200, bottom: 40, left: 80 }}
innerRadius={0.5}
padAngle={0.7}
cornerRadius={3}
activeOuterRadiusOffset={40}
colors={{ scheme: "paired" }}
borderColor={{
from: "color",
modifiers: [["darker", 0.6]],
}}
arcLinkLabelsSkipAngle={10}
arcLinkLabelsTextColor="white"
arcLinkLabelsThickness={2}
arcLinkLabelsColor={{ from: "color" }}
arcLabelsSkipAngle={10}
arcLabelsTextColor="black"
tooltip={CustomTooltip}
defs={[
{
id: "dots",
type: "patternDots",
background: "inherit",
color: "rgba(255, 255, 255, 0.3)",
size: 4,
padding: 1,
stagger: true,
},
{
id: "lines",
type: "patternLines",
background: "inherit",
color: "rgba(255, 255, 255, 0.3)",
rotation: -45,
lineWidth: 6,
spacing: 10,
},
]}
legends={[
{
anchor: "right",
direction: "column",
justify: false,
translateX: 140,
translateY: 0,
itemsSpacing: 2,
itemWidth: 60,
itemHeight: 14,
itemTextColor: "#999",
itemDirection: "left-to-right",
itemOpacity: 1,
symbolSize: 14,
symbolShape: "circle",
},
]}
/>
);
export default PieChart;
This component uses the <ResponsivePieCanvas/> from Nivo to render a responsive pie chart. I also define a custom tooltip to display detailed information about each segment.
Pie chart server
The <PieChartServer/> component fetches data from the /pie-chart API endpoint and returns a <PieChart/> component with the data as prop.
// chart.server.jsx
import PieChart from "./PieChart";
export default async function PieChartServer() {
const response = await fetch("<http://localhost:3000/pie-chart>");
const data = await response.json();
return <PieChart data={data} />;
}
With this done, we now have a beautiful chart:

Bar chart
To avoid extending this blog post with tons of code, implementing a bar chart is like doing it with a pie chart. I had to make a Bar chart server file to fetch the data from the endpoint /salaries, import the Nivo bar chart and return the <BarChart/> component with the data as a prop. The result is this. I’m retrieving salaries from each job posted.

Displaying Statistical Data
To enhance the application's functionality, I 'll display various statistical data such as the minimum and maximum salary and the most frequent job location. This data will be fetched from our API and displayed using a simple but effective UI.
Stats Server
The Stats server component fetches statistical data from the “/stats” API endpoint and passes it to the <Stats/> component.
// stats.server.jsx
import Stats from "./Stats";
export default async function StatsServer() {
const response = await fetch("<http://localhost:3000/stats>");
const data = await response.json();
return <Stats data={data} />;
}
Stats page
The <Stats/> component displays the fetched data in a structured format, providing an overview of key metrics. Im using a Stats component provided by DaisyUI, you can see it here.
The component shows three key pieces of information: the minimum salary, the most frequent job location, and the maximum salary, along with descriptive labels.

Charts page
Finally, with a few classes of TailwindCSS, I was able to place the charts on the same page resulting in a more organized presentation of the data.

Conclusion
Remember, to achieve this, we first scraped the data from the source site here. Then we build an API using NodeJS to retrieve the data from the database here, and finally shaped the data on a site built with NextJS displaying the jobs data and visualizing it using a pie, bar chart, and stats component. This setup provides a solid foundation for a dynamic and interactive user experience. Stay tuned for the next part, where I will explain how to use A.I 🤖 to interact and chat with the DataBase.
Feel free to check out the repository link if you'd like to see the complete code or follow along with the project.