I everyone, I recently undertook a fascinating project: building a
SpaceX launch tracker using Next.js and Tailwind CSS. The aim was to
create a clean, responsive application that pulls data from the SpaceX
API, enabling users to explore launch information and curate their
favorite launches. This project provided a fantastic opportunity to
delve deeper into Next.js's app router and the utility-first
approach of Tailwind. Let's explore the development process and
some key insights gained.
The technical test:
FRONTEND EXERCISE
The exercise consists of building an
application to view Spacex’s latest launches, and being able to
add them to a “favourite” list to revisit them later.
The
application should be developed using React 16.8.4 or further.
Expectations
On the first app load the candidate should hit the following endpoints
from SpaceX to retrieve information about rockets and
launches:
https://api.spacexdata.com/v3/rockets
https://api.spacexdata.com/v3/launches
Information about API usage can be found here:
https://docs.spacexdata.com
The candidate should combine the information to get a result similar to the following:
{
flight_number: 39,
mission_name: "NROL-76",
launch_date_unix: 1493637300,
details: "launch details",
mission_patch: <image_URL>,
rocket: {
rocket_id: "falcon1",
rocket_name: "Falcon 1",
active: true,
cost_per_launch: 6700000,
company: "SpaceX"
}
}
This is a list of functionality the candidate needs to include in the app order by priority,
there’s no need to do everything, let’s include as much as you can:
-
Fetch endpoints and merge arrays (we need to populate the rocket
data of the
launches with the rockets array)
- Display launches
- Favourite functionality (persist them on local storage)
- Search by mission name
- Inspect a specific launch detail
- Pagination
Wireframes: SpaceX Figma
Let's break down how we'll implement this SpaceX launch tracker using NextJS and Tailwind CSS. We'll follow the wireframes as a guide while adding our own modern touches to create an engaging user experience. Our focus will be on building a responsive, performant application that makes it easy to explore and track SpaceX launches.
Setting Up the Foundation - Tech Stack and Initial Steps
I started by setting up a fresh Next.js project with TypeScript
and ESLint, following the official documentation. Then came the
integration of Tailwind CSS, which proved to be incredibly efficient
for styling. I extended the Tailwind theme to match the color palette
specified in the Figma wireframes, ensuring a consistent and visually
appealing design. For custom fonts, I leveraged Next.js's localFont
package to include the D-DIN font
family.
Let's start from the beginning, Installing the NextJs and Tailwind with dependencies, as it’s described by the documentation in
https://nextjs.org/docs/app/getting-started/installation
https://tailwindcss.com/docs/installation/framework-guides/nextjs
npx create-next-app@latest spacex-launches-webapp --typescript --eslint --app
cd spacex-launches-webapp
At this point we get the initial scaffolding of a nextjs app, so the next steps is install tailwind
npm install tailwindcss @tailwindcss/postcss postcss
Component-Driven UI Development
I adopted a component-driven architecture, building reusable components like buttons, icons, paginators, and tabs. This approach not only made the code more maintainable but also accelerated the UI development process. Tailwind's utility classes were instrumental in creating a clean and responsive design, allowing me to translate the Figma wireframes into reality efficiently. I paid close attention to the details of the UI, to make it as close as possible to the figma design, and still keep it responsive
Updating the tailwind config json and add the colors describe in the figma components to maintain an consisten palete color - theme:

The tailwind theme result:
colors: {
"app-bg": "#121212",
"app-surface": "#2C2C2E",
"app-white": "#ffffffde",
"app-gray": {
100: "rgba(255, 255, 255, 0.77)",
200: "rgba(255, 255, 255, 0.67)",
300: "rgba(255, 255, 255, 0.57)",
400: "rgba(255, 255, 255, 0.47)",
500: "rgba(255, 255, 255, 0.37)",
600: "rgba(255, 255, 255, 0.27)",
700: "rgba(255, 255, 255, 0.17)",
},
"app-dp": {
100: "rgba(30, 30, 30, 1)",
200: "rgba(35, 35, 35, 1)",
600: "rgba(63, 63, 63, 1)",
280: "rgba(70, 70, 70, 1)",
300: "rgba(75, 75, 75, 1)",
},
},
extend: {
backgroundImage: {
"app-gradient": "linear-gradient(180deg, #121212 64.11%, #1E1E1E 100%)",
"detail-gradient": "linear-gradient(0deg, #00000075, #ffffff26)",
}
To implement the fonts specified in the Figma design, use
the Next.js localFont package as shown below:

Son we need to set the D-Din Regular and Bold font, so we are going to use the nextjs font package, as following:
import localFont from "@next/font/local";
// Font files can be colocated inside of `assets`
const dDinFont = localFont({
src: [
{
path: "../assets/fonts/d-din/D-DIN.woff2",
weight: "400",
style: "normal",
},
{
path: "../assets/fonts/d-din/D-DIN-Bold.woff2",
weight: "600",
style: "normal",
},
],
});
We have to define the global context to manage and transversal
state, here we have the minimum defined stated that the app need to
works as MVP:
export interface LaunchesContextProps {
launches: Launch[];
paginatedLaunches: Launch[];
setLaunches: React.Dispatch>;
currentPage: number;
setCurrentPage: React.Dispatch>;
totalPages: number;
isLoading: boolean;
favorites: Launch[];
setFavorites: React.Dispatch>;
addFavorite: (launch: Launch) => void;
removeFavorite: (flightNumber: number) => void;
}
const LaunchesContext = createContext(
undefined
);
export const useLaunches = () => {
const context = useContext(LaunchesContext);
if (!context) {
throw new Error("useLaunches must be used within a LaunchesProvider");
}
return context;
};
API Integration and Data Management: 🚀
A crucial part of the project was fetching and merging data from
the SpaceX API. I created a dedicated fetchLaunches
function with flexible query parameters to handle various API
requests. To manage the application's state, I implemented a
global context using React's useContext
hook. This
allowed for seamless data sharing across components, including launch
data, pagination, and favorites.
I defined and single function to access to the api of SpaceX:
export const fetchLaunches = async (
options: FetchLaunchesOptions = {}
): Promise<FetchLaunchesResponse> => {
const { id, limit, offset, sort, order } = options;
const queryParams = new URLSearchParams();
if (id !== undefined) queryParams.append("id", String(id));
if (limit !== undefined) queryParams.append("limit", String(limit));
if (offset !== undefined) queryParams.append("offset", String(offset));
if (sort !== undefined) queryParams.append("sort", sort);
if (order !== undefined) queryParams.append("order", order);
const queryString = queryParams.toString();
const url = `${config.API_URL}/launches${
queryString ? `?${queryString}` : ""
}`;
try {
const response = await fetch(url);
const data: Launch[] = await response.json();
const totalPages = Math.ceil(190 / (limit ?? 1));
return { launches: data, totalPages };
} catch (error) {
console.error("Failed to fetch launches", error);
throw error;
}
};
One of the main challenges was handling API errors gracefully
and implementing robust loading states. I added error handling to the
fetchLaunches
function and implemented loading
indicators to provide a smooth user experience. Additionally, I
focused on optimizing the pagination logic and search functionality to
ensure efficient data retrieval and filtering. Managing local storage
for favorites was another key aspect, requiring careful serialization
and deserialization of data.
Components
I develop the commons components for this app, that includes Buttons,
Icons, Paginator, Tabs, ect.

At this point we have everything that we need to start the UI development, so I define 2 main pages: Home with the list of Launches that we obtain from the API with a search input to filter the results, pagination and two tabs with results and favorites and the Details Page to show the details of the Rocket of the Launch selected.

Using Tailwind to define the UI
The most important thing I learned this time is just get fun while use
predefined tailwind classes to develop the UI of the app, it’s fun and
easy (Once you understand it lol), for example look at how I define
the background defined on theme config:
return (
<header className="relative" style={{ aspectRatio: "1440 / 555" }} >
<nav className="container flex justify-between items-center relative z-30 h-20">
<button
className="rounded-full w-10 h-10 bg-app-surface flex justify-center items-center"
onClick={goBack}
>
<ArrowBackIcon />
</button>
<button
className="rounded-full w-10 h-10 bg-app-surface flex justify-center items-center"
onClick={handleFavoriteClick}
>
<StarIcon isActive={isFavorite} />
</button>
</nav>
<div className="container m-auto max-w-3xl top-64 relative z-30 text-white mt-20">
<div className="launch-date">{launchDate}</div>
<h1 className="text-4xl font-bold text-white uppercase">
{launchName}
</h1>
<div className="text-2xl max-w-3xl overflow-hidden text-ellipsis whitespace-nowrap max-h-12">
{launchDescription}
</div>
</div>
<div className="absolute w-full h-full bg-detail-gradient z-20 top-0"></div>
<div
className="launch-image absolute w-full h-full bg-cover bg-center z-10 top-0"
style={{ backgroundImage: `url({{launchImage}})` }}
></div>
</header>
);
Learnings and Takeaways:
This project reinforced the power of Next.js and Tailwind CSS in
building modern web applications. The app router of Nextjs is very
powerful, and easy to use. I also gained valuable experience in API
integration, state management, and component-driven development. I
learned that having a well defined type system using typescript is
very important to avoid bugs. I also learned to use tailwind
efficiently, and how fast I can create a UI that looks great. I'm
excited to apply these learnings to future projects.
Feel free to explore the project on
spacex-launches-webapp.
I deployed the app on Vercel, you can check it out here.
I'm always open to feedback and collaboration. If you're
a recruiter or potential client looking for a developer with
experience in Next.js, Tailwind CSS, and API integration, let's
connect!