Structuring the Front End: Building Wooster's User Interface
Frontend Adventures: Making Wooster Look Presentable
After getting the backend sorted and teaching Wooster to generate coherent travel plans, it was time to give him a proper home on the web. But before diving into styling or state management, I needed to lay down a solid foundation. Past experience has taught me that rushing into implementation without proper architecture is like trying to teach Wooster tricks before he knows "sit" - it rarely ends well.
Component Architecture: Every Dog Needs a Good Structure
While it might have been tempting to throw everything into App.tsx and call it a day (we've all been there), I decided to start with a proper component structure that would scale with the application:
src/
├── components/ # Organized by feature
│ ├── activities/
│ ├── auth/
│ ├── destinations/
│ ├── explore/
│ ├── layout/
│ ├── shared/
│ ├── trip/
│ └── ui/
├── context/ # Application context
├── hooks/ # Custom hooks
├── lib/ # Utility functions
├── pages/ # Route components
├── services/ # API service layer
├── store/ # State management
├── types/ # TypeScript definitions
└── utils/ # Helper functions
This structure might look like overkill for an MVP, but I've learned the hard way that "I'll organize it later" joins "I'll document it later" in the graveyard of good intentions. By organizing components by feature rather than type, I could work on entire features without jumping between folders. The separation between components/ui and feature-specific components has already paid dividends - reusable UI components stay clean and focused, while feature components can evolve with their specific needs.
The layout implementation set the tone for the entire application:
This layout already accounts for both desktop and mobile views - although I'll admit I made the rookie mistake of not starting mobile-first. Future me will remember that responsive design is easier when you start small and scale up, rather than trying to squeeze a desktop layout into a mobile view later.
Starting Static: Teaching Wooster to Sit Before He Can Fetch
Before adding any interactivity or state management, I built out every component as a static version. This approach might seem like extra work, but it's actually saved me countless hours of debugging by separating concerns: if the layout is broken, I know it's not because of state management or data fetching - it's just the component itself.
Take the destination creation flow, for example. Here's how it started:
No state, no form handling, just pure structure. This made it easy to get the layout and user flow right before adding complexity. Later, it evolved into:
The Wow Factor: Every Dog Needs Its Show-Stopping Trick
During my years in software sales, I learned something crucial: users make snap judgments about software in the first few seconds. You need that "wow" moment - what UX designers often call a hero feature or marquee component - to capture attention immediately.
For Wooster, this meant building something unexpected. Sure, I could have started with the essential trip planning features, but I wanted users to feel that sense of adventure the moment they landed on the explore page. The solution? A fully interactive 3D globe that brings your travel destinations to life:
Each destination appears as point on the globe, with smooth animations as users explore different locations. When you select a destination, the globe gracefully rotates and zooms to give you a closer look. It's not just a visual gimmick - it makes the whole experience of planning travel feel more tangible and exciting.
The explore page brings this all together:
Was implementing a 3D globe essential for an MVP? Absolutely not. But it transformed the experience from "just another travel planner" into something that makes users want to explore. Sometimes the non-essential features are what give your app its personality - and in Wooster's case, that playful, adventurous spirit is exactly what I wanted to capture.
Core User Journey: Building the Happy Path
With the foundation in place, I focused on the essential user journey. Rather than getting lost in edge cases and nice-to-haves, I mapped out the core path: login → explore destinations → create trip → view itinerary.
The login screen started life almost comically simple:
No authentication, no error handling, just a direct path into the application. This might seem too simple, but it let me focus on what mattered: the core user experience. Authentication could come later (and it did, thanks to Supabase making it relatively painless).
Each page followed the same pattern: build the static structure, ensure the layout works, then gradually add interactivity. The explore page was particularly interesting because it had to balance the wow factor of the globe with actual usability:
What I Actually Learned
- Start with structure
- Build static before adding interactivity - it forces you to think about component composition
- A strong "wow factor" feature can be worth the early investment
- Organize by feature, not type - keeps related code together
- Separation of concerns from day one saves refactoring later
- Next time, I'll start with mobile-first design (lesson learned!)
- TypeScript and good folder structure are like training wheels - they might slow you down at first, but they prevent a lot of falls
- Don't be afraid to keep things simple initially - you can always add complexity later
- Document your component structure early - it helps maintain consistency as the app grows
Looking Back
Would I do some things differently? Absolutely. The mobile-first approach is the biggest one - retrofitting responsive design is never fun. But the core decisions - starting with good architecture, building static components first, and investing in that wow factor - have all paid off.
The foundation is solid, but now comes the interesting part: making it all work together seamlessly. That's where state management comes in...
Next up: "State Management: Teaching Wooster to Remember Things" (where we'll dive into the context vs reducer debate, why TypeScript saved my sanity, and how we kept our state as well-organized as our components)