Hey! In this post I'll share my journey building SwiftShelf, a community-driven SwiftUI component library.
Little storytelling: it was another late night browsing GitHub, searching for SwiftUI component inspiration. I had just started working on a new project and needed a beautiful login form. I opened dozens of repositories, scrolled through endless READMEs, and clicked through countless code files just to see what the components looked like.
There had to be a better way.
That frustration is what sparked SwiftShelf.
The Initial Idea
I've been working with SwiftUI for a while now, and I've always loved how the community shares code and helps each other. But something was missing. When I needed UI inspiration or a specific component, I found myself:
- Searching through GitHub repos with no previews
- Opening Figma files that had designs but no code
- Bookmarking random Stackoverflow threads with code snippets
- Losing track of components I had seen before
I thought: what if there was a place where I could just see beautiful SwiftUI components with their code, ready to copy and use? Not a design system, not a tutorial site, but a simple gallery of production-ready components.
That weekend, I started building SwiftShelf.
The Vision (And My First MVP)
My initial vision was simple:
- Show beautiful screenshots of components
- Provide copy-paste ready SwiftUI code
- Make it community-driven (I couldn't build everything alone)
- Keep quality high with some kind of validation
- Make it fast and scalable
I built the first version in about three days. It was basic: a Next.js app with a few components I had created myself. The folder structure was simple, each component had a YAML file for metadata, a Swift file with the code, and a screenshot. I deployed it to Vercel, and it worked!
I shared it with a friend, and he liked it but asked: "How others can contribute?"
That’s when I realized I needed to carry that forward.
My First Big Mistake
Here's where things got interesting (and a bit embarrassing).
I knew I needed to load component data somehow. My initial thought was: "I'll just write a script that reads all the component folders, parses everything, and generates a TypeScript file with all the data. Easy!"
So that's exactly what I did.
I created a build script that:
- Read all snippets from the
/snippets/
folder - Parsed YAML metadata, Swift code, and screenshots
- Generated a massive
snippets-data.ts
file with everything embedded - Bundled it all with Next.js
I ran the script, added 4 components, and checked the generated file.
845 lines of code.
I stared at my screen. Four components. 845 lines. That's over 200 lines per component!
I did some quick math: if each component is ~200 lines, then 100 components would be 20,000 lines. The JavaScript bundle would be massive. Build times would be terrible. Every time someone added a component, the git diff would be huge.
I sat back in my chair and thought: "This is not going to work."
Testing My Fears
I decided to test my theory. I added a few more components and ran the build.
The results were worse than I expected:
- Build time: 45 seconds (and growing)
- JavaScript bundle: Already 8MB with just a handful of snippets
- Page load time: Nearly 4 seconds on a fast connection
Imagine this with 100 components. Or 1,000. The site would be unusable.
I had built something that technically worked, but couldn't scale past a dozen components. This was supposed to be a community-driven platform. I needed to go back to the drawing board.
The Breakthrough
I spent the next few days researching solutions. I looked at how other platforms handle large amounts of dynamic content. I read about incremental static regeneration, edge functions, and various data loading strategies.
Then it hit me while I was reading the Next.js docs about Server Components: I don't need to generate a TypeScript file at all.
What if, instead of generating code, I just... read the files at build time?
The idea was simple but powerful:
- Keep the file-based structure (it's intuitive for contributors)
- At build time, use Server Components to read the files directly
- Pass only the necessary data to the client
- Let Next.js handle the static generation
I rebuilt the entire data loading system in a few days.
The New Architecture: Server Components to the Rescue
Let me explain the architecture in detail, because this is where the magic happens. Please scroll right to see all:
The Old Flow (Static Generation): Build Script → Read Files → Generate TypeScript File → Bundle with Next.js → Huge Bundle
The New Flow (Dynamic Loading at Build Time) Next.js Build → Server Component → Read Files Directly → Generate HTML → Small Bundle
The new architecture uses Next.js Server Components to read files during the build process. The server component loads all snippet data at build time, parses the metadata and code, and generates static HTML with embedded data. On the client side, users receive pre-rendered HTML with only about 120KB of JavaScript for UI interactions.
How It Actually Works
The key insight was that the data loading function runs at build time on the server. File system access happens during next build
, not when users visit the page. Next.js generates static HTML with data already embedded.
The implementation is quite straightforward - it reads component files directly from the filesystem during the build process and passes the data to the client components. Since the code is open source, you can check out the full implementation details in the GitHub repository.
I held my breath and ran the build.
The Results
I watched the terminal nervously as Next.js built the site. 15 seconds.
The build finished in 15 seconds. Down from 45.
I opened the browser and checked the bundle size: 120KB. Down from 8MB.
I refreshed the page. It loaded almost instantly. Under a second. I sat back and smiled. This was it.
Here are the results:
Build Time:
~45s → ~15s (3x faster)
JavaScript Bundle:
~8MB → ~120KB (98.5% smaller)
First Load Time:
~4s → ~0.8s (5x faster)
Generated Code:
20,000+ lines → 0 lines (infinite % better)
But the real victory? I did some quick projections. With this new architecture, I could handle 10,000+ components without any performance issues. The build time would grow linearly at only ~0.1s per component, and the bundle size would stay constant.
This was the scalability I needed.
The Next Challenge: Organizing Code
With the performance issues solved, I moved on to the next challenge: how should contributors organize their code?
I looked at my first few components. Some had multiple files (view, model, extensions). Others were single files with everything together. Some used // MARK:
comments to organize sections, others didn't.
I realized this would be confusing for contributors. Do they submit one file or multiple? How should they name things? What's the expected structure?
Then I had an idea: what if contributors could just write their code naturally in a single file, and SwiftShelf would automatically organize it for display?
Building the Swift Parser
I spent the next few days building a custom Swift parser. It's not a full compiler (that would be overkill), but it's smart enough to understand Swift code structure.
What it does:
- Detects
// MARK:
comments that iOS developers already use - Identifies code structures (struct, enum, class, extension, #Preview)
- Splits the code into logical sections
- Generates appropriate filenames for each section
Here's an example. A contributor writes their Swift code with // MARK:
comments to organize different sections. SwiftShelf automatically detects these sections and displays them as separate tabs with appropriate filenames like SnackbarType.swift
, SnackbarView.swift
, and Preview.swift
.
The Parser Implementation
The parser algorithm is straightforward:
- Split code into lines
- Look for
// MARK:
comments to identify section boundaries - If no MARK comments, detect code structures (struct, enum, class, extension)
- Group related lines into sections
- Generate appropriate filenames from section titles
The beauty of this approach? Contributors don't need to think about organization. They just write clean Swift code with MARK
comments (which they should be doing anyway), and SwiftShelf handles the rest.
On the UI, each section is displayed in a separate tab with syntax highlighting, making it easy to copy individual files.
Making It Community-Ready
At this point, SwiftShelf was fast, the code organization was automatic, and the site looked good. But there was one more critical piece: how do I maintain quality when anyone can contribute?
I've contributed to open source projects before. I know how frustrating it can be when your PR sits for days waiting for review, especially when the issues are just formatting or missing metadata.
I wanted SwiftShelf to be different. I wanted contributions to be smooth and fast.
Automated Validation to the Rescue
I spent a weekend building a comprehensive validation system. Every time someone opens a pull request, a GitHub Action automatically runs checks.
Here's what gets validated:
1. Folder Structure
- Format:
component-name.username
(kebab-case) - Username must match GitHub username in metadata
2. Metadata (meta.yml)
- Required fields:
title
,author
,github-username
,tags
,description
- Tags must be from the allowed list
- Maximum of 3 tags per snippet
- Valid YAML syntax
3. Swift Code (snippet.swift)
- Must
import SwiftUI
- Must define at least one
View
struct - Valid Swift syntax (basic checking)
4. Screenshot (screenshot.png)
- Must be PNG format
- Maximum 500KB file size
- Recommended 9:16 aspect ratio (mobile-first)
- Dimensions: 800-2000px width, 1000-3000px height
The Validation Script
The validation logic is comprehensive and checks four main areas: folder structure, metadata completeness, Swift code requirements, and screenshot specifications. It ensures contributors follow the exact format needed for the platform to work smoothly.
The full validation script is available in the GitHub repository - it's quite detailed and handles everything from YAML parsing to image dimension checking.
What makes this great:
- Clear error messages: Tells you exactly what's wrong
- Helpful suggestions: Suggests similar valid tags using string similarity
- Fast feedback: Runs in GitHub Actions within seconds
The validation provides clear, actionable feedback. For example, it might tell you that a tag is invalid and suggest alternatives, or that a screenshot is too large and needs to be compressed.
The Magic Moment
Here's what happens when someone contributes now:
- They fork the repo and add their component
- They open a pull request
- Within seconds, the validation runs automatically
- If everything passes, I know the PR is technically sound
- I review the content quality (not the technical details)
- Once merged, Vercel automatically deploys, the component is live within minutes
No more back-and-forth about file formats. No more "please add this field to the metadata." The contributor gets instant feedback, and I can focus on content quality rather than technical details.
This was the key to making SwiftShelf truly community-driven.
The Technical Foundation
For the tech stack, I went with tools I know and trust:
Frontend:
- Next.js 14 with Server Components (this was crucial for the performance wins)
- TypeScript (because I hate runtime errors)
- Tailwind CSS (fast styling, great DX)
- Radix UI for accessible components
Build & Validation:
- Node.js for build scripts
- js-yaml for parsing metadata
- ImageMagick for image validation
- Custom Swift parser I built
Deployment:
- Vercel for hosting (automatic deployments are amazing)
- GitHub Actions for running validation on PRs
The entire build process is just two steps: copy images, then build with Next.js. Simple and fast.
Keeping It Simple: No Database Required
One decision I'm really happy with: SwiftShelf has no database.
Every component lives in the repository as three simple files: a metadata file with title and tags, the Swift code file, and a screenshot for visual preview.
This structure is:
- Simple: Anyone can understand it in 30 seconds
- Git-friendly: Each component is isolated, easy to review
- Zero configuration: No database setup, no API keys, nothing
- Automatic attribution: The folder name includes the creator's username
Sometimes the simplest solution is the best solution.
Key Technical Decisions
Building SwiftShelf involved making several important technical decisions. Here are the ones that mattered most:
Decision 1: File-Based vs Database
Why file-based? Every component lives as files in Git, not in a database.
Benefits:
- Zero configuration (no database setup)
- Built-in version control (Git)
- Easy offline development
- Free hosting (Vercel)
- Simple rollbacks (
git revert
)
Trade-offs:
- No complex queries (but we don't need them)
- All data loaded at build time (but that's perfect for static sites)
Decision 2: Build-Time Loading over Runtime
Why build-time? Instead of loading components from a database at runtime, I load them from the file system during the Next.js build.
Benefits:
- Tiny JavaScript bundle (~120KB)
- Sub-second page loads
- No server required
- Scales to thousands of components
How it works: Server Components read files during next build
, and Next.js bakes the data into static HTML. Users get pre-rendered pages with zero API calls.
Where It Stands Today
As I write this, SwiftShelf has grown beyond what I initially imagined:
- 5 beautiful components (and growing every week)
- Sub-second load times on every page
- 120KB JavaScript bundle (and it won't grow)
- 15-second builds (even with hundreds of components)
- 100% automated validation (zero manual PR reviews for technical issues)
But the numbers aren't the real success story. The real success is seeing other developers find the components useful, seeing PRs from contributors I've never met, and knowing that I built something that actually solves a problem I had.
What's Next?
I have a lot of ideas for SwiftShelf:
Soon:
- Search functionality (finding components quickly as the library grows)
- Better dark mode support (the syntax highlighting could be improved)
- Component variations (showing different states of the same component)
Later (maybe):
- Live code playground (edit and preview SwiftUI code in the browser)
- Version tracking (see how components evolve over time)
- Analytics (understand what components are most popular)
But honestly, I'm in no rush. I want to see how the community uses SwiftShelf first. The best features often come from user feedback, not from a roadmap I made alone in my room at 2 AM.
What I Learned
This project taught me more than I expected:
Sometimes You Need to Fail First
My initial architecture was terrible. But I had to build it, measure it, and see it fail before I could understand why it was wrong. That failure led to a much better solution.
Server Components Are Incredible
I've been using React for a while, but Server Components fundamentally changed how I think about data loading. Reading files at build time instead of bundling everything? Game-changer.
Automation Enables Community
Before building the validation system, I was worried about maintaining quality. Now, contributions are smooth and fast because the boring technical checks are automated. I can focus on content, not formatting.
Start Simple, Then Scale
SwiftShelf has no database. No authentication system. No complex backend. It's just files in a repository. And it works perfectly. I've learned to resist the urge to add complexity before I need it.
Want to Contribute?
SwiftShelf is open source and I'd love your help growing it!
Contributing is simple:
- Fork the repository
- Add your component (following the three-file structure)
- Run
npm run validate
locally - Submit a pull request
If validation passes, your component goes live within minutes. No waiting, no hassle.
Check out the contribution guide for details.
Final Thoughts
Building SwiftShelf has been one of my favorite projects. Not because of the tech stack or the performance numbers, but because I built something that solves a real problem I had.
What started as a late-night frustration browsing GitHub repos turned into a journey of learning about performance optimization, community management, and the power of keeping things simple.
I've learned to embrace failure as part of the process. My first architecture was objectively bad, but building it taught me why it was bad. That's valuable.
I've learned that the best projects often come from scratching your own itch. I built SwiftShelf because I wanted it to exist. Turns out, other people maybe want it too.
If you're building SwiftUI apps, check out SwiftShelf. And if you've built something cool, please contribute. Let's build this together.
Technical Resources
- Live Site: swift-shelf.vercel.app
- GitHub Repository: github.com/luizmellodev/SwiftShelf
- Architecture Documentation: ARCHITECTURE.md
- Contribution Guide: CONTRIBUTING.md
- Mini Article: Read a condensed version of this story