Backpackers have a complicated relationship with their gear. Every gram matters
when you're carrying everything on your back for days or weeks at a time.
Some track weight to the tenth of a gram, while others can tell you exactly how
much lighter their pack gets when they eat a Snickers.
Most backpackers manage this intense relationship with just a list or a
spreadsheet. More advanced hikers might use LighterPack, an OG tool that's
powerful but aging and lacks many modern features.
As a pure passion project, I set out to create a more modern, feature-rich tool
that could replace spreadsheets and other existing tools. Using features from
products I love like Linear's command menu and list layout, Plex's find match
functionality, and LighterPack's 'skip registration' ability, I built a tool that
I can happily use for every trip.
Project Constraints
Building a gear management app might seem straightforward, but backpacking sits
at a fascinating intersection of precision data management and real-world utility.
For starters, data visibility is non-negotiable. I want to know what's in my
pack, be able to sort on it, see which items take up the most space or weight,
and make sure each item is categorized correctly (is it consumable, worn on my
body, or in my pack?).
Next, there's the privacy challenge. A growing trend amongst all products is to
try to gather as much data from their users as possible. Many people prefer to
keep their data private, or simply don't want to enter their email into a
product they're unsure of. I doubt many registered LighterPack users even
realize that all of their packs are indexed on Google, but many also take their
privacy seriously. On the flip side, some people do want to share their setups
with the community or with their social network of choice. We needed to
accommodate both without compromising either experience.
Then came the platform question. My target users primarily use desktop when
planning trips but need mobile access when in the field. For the sake of a
manageable scope while developing this app solo, I've focused on the desktop
experience until I'm more confident in the feature set.
Design inspiration from LighterPack's data model and Linear's command interface
Design-wise, I took inspiration from two main sources: LighterPack's
data organization principles (which users already understood) and Linear's
command-driven, keyboard-first interface (which enabled powerful workflows
without cluttered UI). This combination aimed to create something familiar but
significantly improved.
The anonymous user flow lets you start immediately with full functionality
Anonymous User Experience
I built the entire pack experience to function without server persistence, using
the browser's localStorage API to maintain a complete offline experience. This
approach created some interesting technical challenges, particularly around
data synchronization if a user later decided to create an account.
The system creates a pseudo-user with a locally-generated ID and default
preferences. All packed items, categories, and configurations are then stored
relative to this user ID in localStorage. This approach gives anonymous users
the full application experience without requiring authentication.
For those concerned about losing data, we built a seamless migration path: when
creating an account, we automatically transfer all local data to the server,
preserving their existing setups.
Command Menu Navigation
A gear management app needs to handle hundreds of items across multiple packs
and categories. Traditional navigation patterns quickly become cumbersome with
this many objects to manage and manipulate. Scrolling through a list of 100+
items that have been added and edited over the years is a pain. Using a command
menu, users can search for items by name, category, or description, and quickly
find and add the item they need.
The command menu provides quick access to common actions and navigation
I implemented a command menu system (using the cmdk library) that serves as the central
navigation hub for the application. Instead of navigating through multiple
menus, users can press Cmd+K (or Ctrl+K) to access nearly any function in the
application:
The command menu does more than just navigate—it contextually adapts to the
current state of the application. When viewing a pack, commands for adding
items, changing categories, or modifying weights appear. When on the dashboard,
commands for creating packs or importing data take precedence.
This approach dramatically simplified the interface while making it more
powerful. Instead of cluttering the UI with buttons for every possible action,
users can simply type what they want to do. Early feedback showed that even
less technical users quickly adapted to this pattern and found it significantly
faster than traditional navigation.
For power users, we added some keyboard shortcuts throughout the application.
Combined with the command menu, this created a highly efficient workflow where
experienced users rarely need to reach for their mouse—ideal for planning
sessions where you're rapidly cataloging dozens of items.
Customization and Personalization
Backpackers are particular about their gear—and as it turns out, equally
particular about their software. We built extensive customization options
to let users tailor the experience to their preferences.
Theme settings with light, dark, and system modes
Avatar generation and upload capabilities
export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { // Initialize state with values from localStorage const [themeMode, setThemeMode] = useState<ThemeMode>(getInitialTheme()); const [effectiveTheme, setEffectiveTheme] = useState<EffectiveTheme>( getEffectiveTheme(getInitialTheme()) ); // Update effective theme whenever theme mode changes useEffect(() => { const updateEffectiveTheme = () => { setEffectiveTheme(getEffectiveTheme(themeMode)); }; updateEffectiveTheme(); localStorage.setItem('theme', themeMode); // Set up system theme change listener if in system mode if (themeMode === 'system') { const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); const handler = () => updateEffectiveTheme(); mediaQuery.addEventListener('change', handler); return () => mediaQuery.removeEventListener('change', handler); } }, [themeMode]); // Apply theme to document element useEffect(() => { document.documentElement.setAttribute('data-theme', effectiveTheme); }, [effectiveTheme]); // Additional implementation...};
The system uses a combination of localStorage persistence and system preference
detection to ensure the theme stays consistent across sessions while respecting
user preferences.
For personalization, we implemented avatar generation and customization. Users
can either upload their own profile images or generate unique avatars based on
their username. This is fairly simple, but it's a fun way to personalize the
experience and make sure that user profiles aren't bare.
Account preferences with customizable units and currency options
Technical Architecture
Building a modern web application that works across devices while maintaining
strict type safety and data integrity required careful architectural planning.
PackPkr's technical architecture with React, tRPC, and PostgreSQL
The frontend is built with React and TypeScript, leveraging Radix UI for
accessible, composable components. This combination provided excellent
developer experience while ensuring the UI remained accessible and performant.
For data communication, we used tRPC to create type-safe API endpoints. This
approach eliminated the common disconnects between frontend and backend typing,
as the TypeScript interfaces are shared across the entire application:
The backend uses Node.js with PostgreSQL for data persistence. We implemented a
connection pooling strategy to optimize database performance, along with
repository and service patterns to keep the business logic cleanly separated:
For deployment, I use Render.com with automatic database migrations through a
custom script. This setup allows for simplified database management, which as a
designer by trade, I'm fairly new to.
Testing is implemented with Jest and React Testing Library, with specific
attention to data consistency and state management. The database access layer is
thoroughly tested to ensure data integrity across all operations. Again, as a
designer, having tests that ensure my data is always consistent is extremely
valuable while I move quickly to build new features.
Initial user feedback highlighted the speed and precision of PackPkr
Outcomes and Lessons
Several key lessons emerged from this project:
Quality over quantity. Overall, I'd much rather have fewer features that
work perfectly than more features that don't get used. This is harder
than it sounds when you have a long list of ideas that sound like great additions.
Privacy isn't just about security. The anonymous mode wasn't originally
planned as a core feature, but looking at recent competitors' launches it was
something that was commented on by many users.
Command patterns reduce cognitive load. The command menu approach
dramatically simplified the interface while making it more powerful. It's still
a new pattern, so users need to be habituated to it, but it's a powerful way to
make complex workflows more accessible.
TypeScript pays dividends. The investment in strict typing across the entire
application prevented countless bugs and made feature development significantly
faster as the project grew.
Building bcpk stretched my capabilities as both a designer and developer.
Balancing the precision needs of ultralight backpackers with the simplicity
required by casual users forced constant reconsideration of interface patterns
and data models.
The application continues to evolve based on user feedback, with plans to add
trip planning features, weather integration, and expanded sharing capabilities.
What began as a focused tool for gear management is growing into a comprehensive
platform for the backpacking community—all while maintaining the precision,
privacy, and power that defined the original vision.