DEV Community

Voke
Voke

Posted on

Building in Public: CV Analyzer - Act 2 · Scene 2 : Revisiting the App.ts

Click here for Act 2 · Scene 1

Table of Contents

In this scene, I wire up a real React application using React Router v6.4+, turning App.tsx into a routing hub that connects layouts, pages, and error handling so the UI finally renders like a real product.

Overview:

In the previous scene, I introduced a proper layout system using RootLayout and a persistent MainNav. Structurally, everything was in place, but visually, nothing showed up.

In fact, to be very specific, I built the frame. And it comprised of; The navigation and The Layout. But for some reason, the app said: “nice try fella, but I aint gonna render anything”.

Frustrating right?

This scene focuses on connecting the pieces:

  • Defining routes
  • Introducing a home page
  • Handling invalid routes
  • Making the layout actually render

First things first: a proper Home

Rather than leaving placeholder content inside the App.tsx, I introduced a dedicated HomePage component. Simply because that Voke is going in… text sitting inside App.tsx had overstayed its welcome. So I pulled it out into its own HomePage component.

This would ensure App.tsx handles routing and configuration, and Pages define structure. While Components handle UI. Each would have a responsibility of its own.

So the Home page looked like this:

const HomePage = () => {
  return <h2>Voke is going in...</h2>;
};

export default HomePage;
Enter fullscreen mode Exit fullscreen mode

Not to worry, I come back to this in a minute.

Error Handling with an Error Page

Next, I introduced an ErrorPage component to handle undefined routes and routing errors. This is an answer to a question that kept hitting me like a punching bag receiving jabs from Balrog in Street Fighter.

And that is: what happens when someone types a random URL?
Instead of letting the app break silently, I introduced an ErrorPage.

Not flashy. Just responsible.

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

const ErrorPage = () => {
  return (
    <>
      <MainNav />
      <main style={{ width: "100%", maxWidth: "70rem", margin: "5rem auto" }}>
        <h2>Page not found</h2>
        <p>
          <Link to="/">Go back home</Link>
        </p>
      </main>
    </>
  );
};

export default ErrorPage;
Enter fullscreen mode Exit fullscreen mode

This ensures:

  • Invalid URLs don’t crash the app
  • Users always have a recovery path
  • Navigation remains consistent even on error states.

So in plain English, if a route doesn’t exist, the user sees the navigation, gets a clear message, and has a way back home.

That’s basic UX hygiene. And it’s easier to do now than later.

Structuring App.tsx

Now comes the heart of the app.

With pages in place, App.tsx became the central routing configuration using React Router v6.4+. I intentionally chose React Router v6.4+ because it introduces the data router APIs (createBrowserRouter). This gives me a more scalable routing setup with first-class support for layouts and error boundaries. So I stuck with v6.4+.

And it’s best I spell this out here. App.tsx isn’t a UI file anymore, it’s now the traffic controller.

This is how the App.tsx file looks like now:

import { createBrowserRouter, RouterProvider } from "react-router-dom";
import RootLayout from "./pages/RootLayout";
import HomePage from "./pages/HomePage";
import ErrorPage from "./pages/ErrorPage";

function App() {
  const router = createBrowserRouter([
    {
      path: "/",
      element: <RootLayout />,
      errorElement: <ErrorPage />,
      children: [{ path: "/", element: <HomePage /> }],
    },
  ]);

  return <RouterProvider router={router} />;
}

![ ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/89v2pxmr9suj8n54hnf0.png)


export default App;
Enter fullscreen mode Exit fullscreen mode

At this point:

  • Layouts were nested correctly
  • Routes were declarative
  • Errors were handled centrally

This simply meant that the application was beginning to have a pulse. Did I hear some say breathe [in the voice of the uncredited singer of the fabolous song breathe].

So if I save and reload the page, I will get this:

UI Consistency: Global CSS Reset

As you can see from the image above, once everything was rendered, spacing issues became obvious. The layout felt… off. This happened because default browser margins and padding were creeping in

A quick global reset fixed it:

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
Enter fullscreen mode Exit fullscreen mode

With that code block added in, saw this:

This brought the layout back under control and gave the UI a predictable baseline to build on.

Refining the Home Component

At this point, it felt wrong to keep UI markup inside HomePage.
Pages shouldn’t worry about how things look, they should worry about where things live.

So I pushed the UI one level down and introduced a dedicated Home component which also had its own style file.

HomePage now handles routing, while Home focuses purely on presentation.

The Home component:

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

const Home = () => {
  return (
    <div className={classes["home-container"]}>
      <h1>Analyze candidate-job fit using AI</h1>
      <p>Get instant insights on CVs and job descriptions with AI</p>
    </div>
  );
};

export default Home;
Enter fullscreen mode Exit fullscreen mode

This component is then rendered inside HomePage, keeping concerns separated.

That separation already feels like it will pay off later.

Saved it, and checked my browser and saw this:

That's more like it...

Mental Model

At this stage:

  • App.tsx is the heart
  • RootLayout can be compared to a skeleton
  • While pages are meant for destinations
  • And Components render the presentations

Well, they all seem to play a role. They all know their role, like the video game tagline SmackDown 2: Know your role. 😊

And with all these, the foundation seems stable.

Why This Scene Matters

This scene demonstrates that I:

  • Understand modern React Router architecture (v6.4+)
  • Separate configuration, layout, pages, and UI cleanly
  • Build structure before features
  • can refactor when needed

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

We in the Building…
Building in Progress…

Top comments (0)