DEV Community

Voke
Voke

Posted on • Edited on

Building in Public: CV Analyzer - Act 2 · Scene 1 - Laying Down the App’s Spine (Main Navigation)

Click here for Act 1 · Scene 2

Table of Contents

Overview

Let’s keep the train moving, shall we?

After cleaning up the template in Act 1 and letting you all know that
Voke is going in…, I hit that familiar moment every builder knows:
The app technically exists… but it doesn’t feel like anything yet.

There’s

  • No structure.
  • No sense of direction.
  • Just a bland page staring right back at you.

So instead of rushing into “AI” or “analysis logic”, I stopped and asked a boring but important question:

If this were a real product, what would always be on the screen?

The answer was obvious: navigation.

So, before going in on all the features that this application would have, it would first need a spine. An app needs a spine.

So in this scene, I focused on building the main navigation not as decoration, but as a permanent part of the product’s layout.

Are you with me? If so, let’s go…

Building the MainNav (basic setup)

So I went into the src folder and created a components folder, then a main-nav folder inside it. In the main-nav folder, I created a file called MainNav.tsx. It also had a brother called MainNav.module.scss.

Here’s what the first version of MainNav.tsx looks like:

import classes from "./MainNav.module.scss";

const MainNav = () => {
  return (
    <header className={classes.header}>
      <h1>
        <span>AI Talent Profile</span> Analyzer
      </h1>

      <nav>
        <ul>
          <li>Auth</li>
          <li>AnalyzeCandidate</li>
        </ul>
      </nav>
    </header>
  );
};

export default MainNav;
Enter fullscreen mode Exit fullscreen mode

At this point:

  • There’s no routing
  • There’s no auth logic
  • And no conditions

With what we have up there in the code block, that already answers the question of what this application is about.

I kept the styles local using an SCSS module:

.header {
  width: 100%;
  height: 5rem;
  display: flex;
  align-items: center;
  justify-content: space-between;

  background-color: rgb(242, 237, 237);
  padding: 0 10%;

  nav {
    ul {
      display: flex;
      gap: 1rem;
      list-style: none;
      margin: 0;
      padding: 0;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Nothing groundbreaking here in both files. They would get beefier as we continue.

Where Does the Navigation Live?

O.k! I hope it’s not what you guys are thinking. Did I hear someone say:

  • “Easy answer, the navigation lives in the computer”? lol.

And that’s not a jab at your ribs when I say all jokes aside.

But seriously, I saved everything… hot reload kicked in…
And nothing showed up.

Which made sense.

A navigation component by itself doesn’t magically appear in an app. It’s no Harry Potter.
It needs a home, a place that exists above individual pages.

And that brings us to this bad guy, the Root Layout.

Introducing the Root Layout (where things actually come together)

Meet the RootLayout component. A layout wrapper for the app when using React Router v6+.

With the introduction out of the way, a navigation component is useless if it isn’t anchored into the app correctly.

So instead of spraying the navigation across pages, I introduced a RootLayout using React Router’s layout pattern.

This layout defines:

  • What stays on screen permanently
  • What changes when routes change

Here’s the layout:

import { Outlet } from "react-router-dom";
import MainNav from "../components/main-nav/MainNav";

const RootLayout = () => {
  return (
    <>
      <MainNav />
      <main style={{ width: "100%", maxWidth: "70rem", margin: "2rem auto" }}>
        <Outlet />
      </main>
    </>
  );
};

export default RootLayout;
Enter fullscreen mode Exit fullscreen mode

So with all that in place, our darling navigation finally got a place it could call home. Isn’t that cute…😊😊😊.

What Is Outlet and What Does It Actually Do?

<Outlet /> is a React Router component that acts as a placeholder for nested routes.
It tells React Router: Render the matched child route here. And as you can see from the code up there, it’s from the react-router-dom library, so it is meant to handle and deal with page routings.

Mental model

Now we see that:

MainNav lives at the top. It’s always present.
And
Outlet becomes the surface where everything else will evolve.

At this stage, the UI still isn’t doing much. But the mental model is already cleaner.

But still, even with all these, all that we have done isn’t yet visible.

Stay tuned, we will have that sorted out in the next scene.

Why this scene matters

This scene isn’t just about headers or CSS.

It shows that I:

  • Am not rushing to bolt on AI features yet.
  • Am building the frame first, because products collapse when the foundation is rushed.
  • Think in layouts and systems, not just components.
  • Intentionally build things boring first, so they don’t fight me later.

Thanks for reading.
Let’s move on to the Next Scene.

We in the Building…
Building in Progress…

Click here for Act 2 · Scene 2

Top comments (0)