DEV Community

Cover image for Reworking My User Dashboard in Next.js: What I Broke and What I Fixed
Ngbaronye Nmesirionye
Ngbaronye Nmesirionye

Posted on

Reworking My User Dashboard in Next.js: What I Broke and What I Fixed

I spent this week doing something developers are often told not to do: I rewrote a perfectly functional feature.

The user dashboard for Pantero—the offline AI companion I'm building from my FUTO hostel room—worked. It showed the waitlist position. It displayed the referral link. It did its job.

But it felt wrong.

Slow. Clunky. Like it was fighting me every time I opened it. So I tore it down and rebuilt it in Next.js. Here's what I learned.

The Problem with the Old Dashboard

The original dashboard was built early in the project, back when I was still thinking in plain HTML and scattered JavaScript. It was a single, massive component with:

  • Mixed concerns (data fetching, UI rendering, state management all jammed together)
  • Poor mobile responsiveness
  • No loading states—just blank screens while data loaded
  • Hardcoded values that made updates painful

It "worked" for 10 users. At 187+ waitlisters, it was starting to show its cracks.

The Rebuild: Goals

I set three clear goals for the new dashboard:

  1. Speed: Load fast, even on 3G.
  2. Clarity: Users should instantly understand their waitlist position, referral stats, and next steps.
  3. Maintainability: I should be able to add new features without breaking everything.

The Tech Approach

Since Pantero is already a Next.js app, I leaned into the framework's strengths:

Feature Implementation
Data Fetching Server Components for initial load, client-side fetch for real-time updates
State Management React Context for global user state, useState for local UI
Loading States Suspense boundaries with skeleton loaders
Styling Tailwind CSS (already in the project)

The Hardest Part: Balancing Server and Client

The dashboard needs to show:

  • Static data (user's email, join date) → perfect for Server Components
  • Dynamic data (current waitlist position, referral count) → needs client-side updates

I kept running into hydration mismatches because I was mixing server-rendered content with client-updated numbers. The fix was simple once I understood it: keep the dynamic parts isolated in client components and pass static data as props from the server.


`jsx
// Server Component (app/dashboard/page.tsx)
export default async function DashboardPage() {
  const user = await getUser(); // server-side fetch

  return (
    <div>
      <h1>Welcome, {user.email}</h1>
      <WaitlistPosition initialPosition={user.position} />
      <ReferralStats initialCount={user.referralCount} />
    </div>
  );
}

// Client Component (components/WaitlistPosition.tsx)
'use client';

export function WaitlistPosition({ initialPosition }) {
  const [position, setPosition] = useState(initialPosition);

  // Refresh every 30 seconds
  useEffect(() => {
    const interval = setInterval(async () => {
      const newPosition = await fetchPosition();
      setPosition(newPosition);
    }, 30000);
    return () => clearInterval(interval);
  }, []);

  return <p>Your position: #{position}</p>;
}
`

This pattern saved me so much headache.

Before and After
Here's what the dashboard looked like before the rework:


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

And here's the new version (still a work in progress):


![ ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0di2q0wrfowqnjbrg5zh.png)
The new dashboard:

Shows waitlist position prominently

Displays referral link with a one-click copy button

Includes a simple referral count tracker

Has a clear call-to-action to join the WhatsApp Channel

Loads with skeleton states instead of blank screens

What's Still Missing
Animated transitions between states

Dark mode toggle (coming soon)

Real-time referral notifications

Mobile bottom sheet for quick actions

The Bigger Picture
This dashboard rework isn't just about making things pretty. It's the foundation for everything coming next:

Verified credential generation

AI mentorship history

Community leaderboards

If the dashboard felt clunky at 187 users, it would have collapsed at 1,000. Now I have a solid base to build on.

Current Pantero Metrics
Metric  Count
Waitlist    187+
Countries   Nigeria, Ghana, Kenya
Dashboard rebuild time  4 days
Bugs fixed during rebuild   7
New bugs created    3 (working on it)
Join the Waitlist
If you're curious about the offline AI companion I'm building through all this Next.js chaos:

🔗 pantero.vercel.app

The waitlist is open. The dashboard is better. The build continues.

Have you ever rewritten a feature that "worked" because you knew it could be better? How did it go? Let's swap war stories in the comments.
Enter fullscreen mode Exit fullscreen mode

Top comments (0)