Last week, I tried to create a CodeSandbox demo for my npm package. It failed. I tried to create a demo on https://playcode.io/typescript and it failed again.
The error message? ENOSPC: no space left on device
I stared at my screen, confused. Then I checked the package size.
324MB. ๐ฑ
For context, React is about 6KB minified. My simple postal code lookup library was somehow 54,000 times larger.
Here's the story of how I accidentally shipped a massive package to npm, how I fixed it, and what I learned building zero-dependency lookup libraries that are now used by developers worldwide.
๐ค Why I Built This
It started with a simple need: I wanted to validate US ZIP codes in a React app.
"Easy," I thought. "There must be dozens of npm packages for this."
I was right - there were dozens. But each had issues:
โ Some only worked in Node.js - couldn't use them in React
โ Others required API calls - rate limits, network latency, offline failures
โ Many hadn't been updated in years - broken, outdated data
โ Several were huge - 50MB+ packages with tons of dependencies
โ Most lacked TypeScript support - no autocomplete, no type safety
I needed something that:
โ
Works in browsers AND Node.js
โ
Zero runtime dependencies
โ
Full TypeScript support
โ
Returns city, state, county, and coordinates
โ
Actually maintained with fresh data
When I couldn't find it, I decided to build it myself.
Thus, zipcodes-us was born.
(And later, postalcodes-india for Indian postal codes)
๐ป What It Does
Here's the simplest example:
import zipcodes from 'zipcodes-us'
const info = zipcodes.find('90210')
console.log(info)
Output:
{
city: 'Beverly Hills',
state: 'California',
stateCode: 'CA',
county: 'Los Angeles',
latitude: 34.0901,
longitude: -118.4065,
isValid: true
}
โ Try it live in CodeSandbox
More Cool Features
Find all ZIP codes in a city:
const bostonZips = zipcodes.findByCity('Boston', 'MA')
// Returns array of all Boston ZIP codes with full info
Search within a radius:
const nearby = zipcodes.findByRadius(37.7749, -122.4194, 10)
// Find all ZIP codes within 10 miles of San Francisco
Auto-complete addresses:
// User enters ZIP code, auto-fill city and state
const { city, state } = zipcodes.find(userZipCode)
addressForm.city.value = city
addressForm.state.value = state
And it works exactly the same in Node.js, React, Next.js, Vue, or vanilla JavaScript.
๐ The 324MB Disaster
Everything was working beautifully locally. I ran my tests, they passed. I built the package, published v1.1.1 to npm, and went to create a demo.
I opened https://playcode.io/typescript, created a new project, added my package...
npm install postalcodes-india
And then... nothing. The installation hung. Eventually:
Error: ENOSPC: no space left on device
"What?" I thought. "It's just a postal code lookup. How much space could it possibly need?"
I checked unpkg.com to see what was actually published.
My package was 324MB. My US ZIP code package? 79MB.
For a simple lookup library with zero runtime dependencies.
๐ Investigating the Problem
I dove into the published package contents:
dist/
index.js (6.9MB)
index.js.map (15.1MB) โ What are these?
index.esm.js (6.9MB)
index.esm.js.map (15.1MB) โ These look huge
index.umd.js (6.2MB)
index.umd.js.map (11.9MB) โ Oh no...
The culprit: Source map files (.map
files)
๐ฑ What Are Source Maps?
Source maps are debugging tools that map minified/compiled code back to your original source code. They're super useful during development:
Without source maps:
Error at line 1: function a(b){return c(b)}
(What does this mean?? ๐คท)
With source maps:
Error at line 42 in src/index.ts:
function findPostalCode(code) {
return lookup(code)
}
(Ah, now I can debug it! โ )
๐คฆ The Problem
Source maps are typically 2-3x larger than the actual code. And I was:
- Generating them (fine for development)
- Publishing them to npm (not fine!)
My Rollup configuration looked like this:
export default [
{
input: 'src/index.ts',
output: {
file: 'dist/index.esm.js',
format: 'es',
sourcemap: true, // โ Creating massive .map files
}
},
// ... similar for other builds
]
That innocent sourcemap: true
was creating huge .map
files that were being published to npm, even though:
- โ End users don't need them
- โ They can't use them (they're for MY source code, not theirs)
- โ They were making the package impossible to install in many environments
โ The Fix
The solution was surprisingly simple.
Step 1: Update Rollup Config
export default [
{
input: 'src/index.ts',
output: {
file: 'dist/index.esm.js',
format: 'es',
sourcemap: false, // โ Changed from true to false
}
},
// ... same for all builds
]
Step 2: Rebuild
npm run build
I checked the dist/
folder - no more .map
files! โจ
Step 3: Publish
# Bump version
# Update package.json version to 1.1.2
npm publish
๐ The Results
zipcodes-us:
- Before: 79MB
- After: 29.6MB
- Reduction: 62.5% ๐
postalcodes-india:
- Before: 324MB
- After: 137MB
- Reduction: 58% ๐
Now CodeSandbox could actually install them. Crisis averted!
๐ What I Learned
1. Source Maps Are Not Needed in Production
Source maps are development tools. They help YOU debug YOUR code. End users installing your package:
- Don't have access to your source files
- Can't use the source maps anyway
- Just want the compiled code to work
Lesson: Turn off source maps for npm packages (sourcemap: false
).
Exception: Some argue you should ship source maps for debugging in production. If you do, consider:
- Using
.npmignore
to exclude them - Or setting
"files": ["dist/**/*.js", "!dist/**/*.map"]
in package.json
2. Always Check Your Published Package
Before v1.1.2, I never actually looked at what was being published to npm. I assumed Rollup was doing the right thing.
Now I always check:
# See what will be published
npm pack --dry-run
# Or check on unpkg after publishing
https://unpkg.com/your-package@version/
3. Test in Real Environments
My package worked perfectly on my local machine. But CodeSandbox, StackBlitz, and other online IDEs have disk space limits.
If your package is too big, it won't work in these environments - and developers LOVE trying packages in online editors before installing locally.
Lesson: Create a CodeSandbox demo as part of your development process, not after publishing.
4. Package Size Matters
I thought "it's just data, size doesn't matter." Wrong.
Large packages:
- โ Take longer to install
- โ Fill up disk space in CI/CD pipelines
- โ Can't be used in constrained environments
- โ Increase
node_modules
bloat - โ Make developers hesitant to use them
Lesson: Keep packages as small as possible. Every MB counts.
๐๏ธ Other Technical Challenges
Fixing the package size wasn't the only challenge. Here are other things I learned:
Making It Work Everywhere
The hardest part wasn't the lookup logic - it was making ONE package work in:
- Node.js (CommonJS -
require()
) - Node.js (ESM -
import
) - Browsers (via bundlers like Webpack/Vite)
- Browsers (via CDN
<script>
tags)
The Solution: Multiple Build Targets
I use Rollup to create three different builds:
export default [
// ESM for modern bundlers (Vite, Webpack 5)
{
input: 'src/index.ts',
output: {
format: 'es',
file: 'dist/index.esm.js'
}
},
// CommonJS for Node.js
{
input: 'src/index.ts',
output: {
format: 'cjs',
file: 'dist/index.js',
exports: 'named'
}
},
// UMD for browsers (script tags)
{
input: 'src/index.ts',
output: {
format: 'umd',
file: 'dist/index.umd.js',
name: 'zipcodes'
}
}
]
Then in package.json
, I specify which file to use for each environment:
{
"name": "zipcodes-us",
"main": "dist/index.js", // Node.js default (CommonJS)
"module": "dist/index.esm.js", // Modern bundlers (ESM)
"browser": "dist/index.umd.js", // Browser <script> tag
"types": "dist/index.d.ts" // TypeScript definitions
}
Now it just works, everywhere. Developers don't need to think about it.
Keeping Data Fresh
ZIP codes change! The USPS adds new ones, retires old ones, updates boundaries.
I couldn't just ship v1.0 and forget about it. I needed a system to keep data current.
My solution:
- Data comes from GeoNames (updated regularly, CC BY 4.0 license)
- I have a script which I run to check for updates. I try my best to check it daily.
- When changes are detected, I auto-generate a new data file
- Run tests to ensure nothing broke
- Publish a new version to npm
The data check script runs as a GitHub Action. Completely automated.
Lesson: If your package depends on external data, plan for updates from day one.
๐ Results After Publishing the Fix
After fixing the package size and improving documentation:
zipcodes-us:
- ๐ฆ 450+ weekly downloads
- โญ 3 GitHub stars
- ๐ Zero open issues
- ๐ Used in production applications
postalcodes-india:
- ๐ฆ 150+ weekly downloads
- โญ 1 GitHub stars
- ๐ฎ๐ณ Covers all 19,000+ Indian postal codes
Both packages are growing steadily. More importantly, they're actually useful to real developers.
๐ฏ What's Next
I'm working on:
- Performance optimizations - can lookups be even faster?
- More search methods - find by county, region, etc.
- Better documentation - more examples and use cases
- Maybe more countries? - depends on demand
The hardest part about maintaining open source isn't writing code - it's the ongoing commitment to:
- Respond to issues quickly
- Keep data updated
- Write good documentation
- Help users when they're stuck
But seeing my packages help other developers makes it worth it.
๐ Try It Yourself
If you need ZIP or postal code lookups in your project:
For US ZIP codes:
- npm: zipcodes-us
- GitHub: ikarthikng/zipcodes-us
- Demo: CodeSandbox
For Indian postal codes:
- npm: postalcodes-india
- GitHub: ikarthikng/postalcodes-india
- Demo: CodeSandbox
Installation is simple:
npm install zipcodes-us
# or
npm install postalcodes-india
And if you find bugs or have feature requests, I actually respond to GitHub issues! ๐
๐ญ Your Turn
Have you ever shipped something way too big to npm? Made a similar mistake? I'd love to hear your stories in the comments!
And if you're building your own npm packages, here's my advice:
- โ Always check what you're actually publishing
- โ Test in constrained environments (CodeSandbox, etc.)
- โ Turn off source maps for production builds
- โ Keep package size as small as possible
- โ Provide TypeScript definitions (even if you write JS)
- โ Create live demos - developers love to try before installing
Happy coding! ๐
Top comments (0)