惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Vercel News
Vercel News
博客园 - 【当耐特】
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
小众软件
小众软件
Hugging Face - Blog
Hugging Face - Blog
aimingoo的专栏
aimingoo的专栏
WordPress大学
WordPress大学
G
Google Developers Blog
博客园 - 叶小钗
大猫的无限游戏
大猫的无限游戏
P
Proofpoint News Feed
J
Java Code Geeks
U
Unit 42
云风的 BLOG
云风的 BLOG
阮一峰的网络日志
阮一峰的网络日志
N
Netflix TechBlog - Medium
宝玉的分享
宝玉的分享
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
D
Docker
V
Visual Studio Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
H
Help Net Security
V
V2EX
T
Tailwind CSS Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Luminary: a NextJS migration story.
Awoyemi Abiola · 2026-06-27 · via DEV Community

Introduction

We're back. After our four-week sprint that took Luminary from a blank repository to a fully functional community platform built in vanilla HTML, CSS, and JavaScript, we regrouped to migrate the entire project to a modern framework.

Luminary is a community-powered directory and news platform celebrating women who are driving change across all fields — business, science, arts, activism, sport, technology, agriculture, education, and more. It allows anyone to nominate or self-submit, is searchable by name and field, and surfaces daily and weekly news of women making impact globally.

For this two-week sprint, we moved Luminary off the vanilla stack and onto a proper application architecture. I would be writing about the migration in general, my role in it, and the lessons I picked up along the way.

The Migration

The old Luminary lived as a set of standalone HTML pages, which we had wired up with its own script and stylesheet. It worked fine, but as the project grows, sharing logic, keeping styles consistent, and integrating with the backend cleanly is foreseen to become hectic.

This sprint we restructured the project into a Turborepo monorepo with two apps — a Next.js (App Router) + TypeScript frontend and an Express + Supabase backend. On the frontend we adopted Tailwind for styling, Base UI for accessible primitives, TanStack Query for data fetching, and a shared design system of common components. The work was split across feature branches, with each member converting the pages they originally built. My share was the nominations flow and the directory and profile pages.

Week 1

Specific Features Implemented

This week I converted the nominations experience from the old DOM-driven form into the new Next.js stack;

  • Rebuilt the full nomination and self-submission form as composable React components.
  • Extended a shared form component library so every input shares a single source of truth.
  • Wired submissions through TanStack Query and a typed service layer.

Nomination Form Screenshot

Technical Implementation Breakdown

  • Shared form primitives: Extended the common form module into FormField, TextField, FormLabel, and a Base UI SelectField so labels and inputs are defined once and reused everywhere instead of being hand-written per form.
  • Data out of components: Moved form constants — initial state, tabs, and field options — into the data layer, keeping the components focused on rendering.
  • Design system consistency: Replaced raw <button> elements across the tabs, link fields, and collapsible sections with the custom Button component, and added a reusable Drawer wrapper around Base UI's drawer.
  • Service layer: Split the logic into a NominationService and a standalone UploadService, with a private toPayload method and a shared error utility for consistent messaging.
  • Submission flow: Swapped manual try/catch handling for TanStack Query's mutation onError, and conformed API errors to a typed response shape.

Challenges & Resolutions

  • Turning imperative code into components: The original form was built with direct DOM manipulation, so I had to rethink it as composable React components and lift the shared pieces — labels, inputs, button, drawer — into reusable primitives rather than porting the markup one-to-one.
  • Acting on review feedback: A good portion of the week was responding to PR reviews from Isaac, our repo maintainer overlord, tightening the conversion to match our conventions — using the design-system components, removing manual memoization since the project runs the React Compiler, and routing repeated logic through shared utilities.

What I Learned This Week

This week, from converting the nominations flow, I learnt how to think better and make cleaner use of component-driven, design-system-first way, how to model data fetching and submissions with TanStack Query, and how to structure a typed service layer in TypeScript that keeps API concerns out of the UI.

Week 2

This week shifted from forms to data, getting the directory and profile pages reading real records from the backend, and porting the last of my old pages across.

Specific Features Implemented

This week I migrated the directory profile page and connected them to the live API;

  • Ported the old profile page from the vanilla HTML/JS project into a Next.js dynamic route with live data.
  • Added a shared mapping layer between the API and the UI profile shape.

Profile Page Screenshot

Technical Implementation Breakdown

  • Profile route: Created /directory/[id] as an async server component and rebuilt the old profile.html sections — header, about, and related links — as components using next/image and the original decorative photo frame.
  • Shared mapper: Added a single toNomineeProfile mapper feeding both the directory list and a new getProfileById, normalizing inconsistent URL fields coming back from the API.
  • Image configuration: Allow-listed the remote image hosts in the Next.js config so profile and avatar images render through the image pipeline.

Challenges & Resolutions

  • Profiles returning "not found": The directory cards were keyed on the nominee id while the detail endpoint looks records up by nomination id, so links never resolved. I aligned both on the nomination id through the shared mapper.
  • The backend wouldn't boot locally: It was missing its environment file, and once that was added the Supabase client still threw — Node.js below version 22 has no native WebSocket, which the client requires, so I added a ws fallback to provide one.
  • A request black hole: Even with the server running, requests never reached it. Port 5000 turned out to be reserved at the OS level on my computer, so the kernel was answering instead of the app. Moving local dev to a free port fixed it instantly, interestingly this was an issue I experienced in the original project stack as well, but had to debug and resolve again as I had forgotten, higlighting the importance of documentation in projects, silly me.

What I Learned This Week

This week, from migrating the profile pages, I learnt new methods of integrating a typed frontend with a real API across server components, the value of a single mapping layer between API and UI shapes, and the importance of documentation across project sprints.

Conclusion

Two weeks in, Luminary has a more solid foundation — a typed, component-driven frontend, the hard part of the move is behind us. For now, at least, onto the next.

Github Repository

Luminary

Contributors

-Product Lead 1: Ramnan Ramyil
-Product Lead 2: Awoyemi Abiola
-People Manager: Emmanuel Dania
-Lead Maintainer: Isaac Shosanya
-Design Lead: Michael Omonedo
-Engineering Lead: Daniel Chisom
-Backend Lead: Isaac Shosanya