# I Just Published My First npm Package — Here's Everything I Did
Source: Dev.to
A complete walkthrough of publishing Cartlify — a React e-commerce UI kit — to npm for the first time. Yesterday I published Cartlify to npm. npm install cartlify
It sounds simple. But getting to that one line took more decisions, more configuration, and more trial and error than I expected. This article covers everything — from setting up the build config to the actual publish command — so you don’t have to figure it out the hard way. Cartlify is a production-ready React + TypeScript + Tailwind CSS component library focused on e-commerce UI. 4 components that every e-commerce project needs: ProductCard — 3 layout variants, image gallery, wishlist, sale badges, skeleton loading CartDrawer — animated slide-in, focus trap, ESC dismiss, quantity stepper CheckoutStepper — horizontal/vertical, animated connectors, keyboard navigation PageLoader — 4 animation styles, 3 position modes Plus 3 utility hooks, 11 tree-shakeable icons, 40+ CSS design tokens, full dark mode, and 141 Jest + React Testing Library tests. Built so freelance developers and indie makers can skip the painful e-commerce UI layer and ship faster. Before npm, Cartlify was only available on Gumroad as a paid download. That’s fine — but npm adds something Gumroad can’t: Developer sees Cartlify → runs npm install cartlify → evaluates the compiled output → trusts the quality → buys the full source on Gumroad
npm is a credibility and discovery channel — not just a distribution method. A package on npm signals that something is real, maintained, and production-ready. Also: npmjs.com gets millions of developer searches every month. That’s free traffic you can’t get from Gumroad alone. The most important decision before publishing is how you bundle your library. I chose tsup — a zero-config TypeScript bundler built on esbuild. Here’s why:
Tool Config needed Speed Output
Rollup Lots Medium ESM + CJS
Webpack Heavy Slow CJS only
Vite lib mode Some Fast ESM + CJS
tsup Almost zero Very fast ESM + CJS + .d.ts
My tsup.config.ts: import { defineConfig } from ‘tsup’;
export default defineConfig({ entry: [‘src/index.ts’], format: [‘cjs’, ‘esm’], dts: true, sourcemap: false, clean: true, minify: true, external: [‘react’, ‘react-dom’], esbuildOptions(options) { options.alias = { ‘@components’: ’./src/components’, ‘@primitives’: ’./src/primitives’, ‘@hooks’: ’./src/hooks’, ‘@utils’: ’./src/utils’, ‘@types’: ’./src/types’, }; }, });
Key decisions: format: [‘cjs’, ‘esm’] — supports both older and modern bundlers dts: true — generates .d.ts TypeScript type definitions minify: true — compiled output is minified (buyers get clean source on Gumroad) sourcemap: false — no source maps in public npm package external: [‘react’, ‘react-dom’] — don’t bundle React itself After running npm run build, the dist/ folder looks like: dist/ ├── index.js ← CommonJS ├── index.mjs ← ES Module └── index.d.ts ← TypeScript types
This is the most critical file for npm publishing. Every field matters. { “name”: “cartlify”, “version”: “1.0.0”, “description”: “Production-ready React e-commerce UI kit — ProductCard, CartDrawer, CheckoutStepper, PageLoader. Built with TypeScript, Tailwind CSS and Storybook.”, “main”: ”./dist/index.js”, “module”: ”./dist/index.mjs”, “types”: ”./dist/index.d.ts”, “exports”: { ”.”: { “import”: ”./dist/index.mjs”, “require”: ”./dist/index.js”, “types”: ”./dist/index.d.ts” } }, “files”: [ “dist”, “README.md” ], “scripts”: { “build”: “tsup src/index.ts”, “prepublishOnly”: “npm run build && npm run lint && npm run test” }, “keywords”: [ “react”, “typescript”, “tailwind”, “ui-kit”, “ecommerce”, “cart”, “product-card”, “checkout”, “storybook”, “component-library”, “frontend” ], “author”: “Karthik G S ”, “license”: “MIT”, “homepage”: “https://cartlify.vercel.app”, “repository”: { “type”: “git”, “url”: “https://github.com/thirumalai77/cartlify” }, “bugs”: { “url”: “https://github.com/thirumalai77/cartlify/issues” }, “peerDependencies”: { “react”: ”>=18.0.0”, “react-dom”: ”>=18.0.0” }, “devDependencies”: { “tsup”: “^8.0.0”, “typescript”: “^5.0.0” } }
Key fields to get right: files — only publish what buyers need: “files”: [“dist”, “README.md”]
This stops src/, .storybook/, node_modules/, and test files from being included in the npm package. exports — modern bundler resolution: “exports”: { ”.”: { “import”: ”./dist/index.mjs”, “require”: ”./dist/index.js”, “types”: ”./dist/index.d.ts” } }
peerDependencies — don’t bundle React: “peerDependencies”: { “react”: ”>=18.0.0”, “react-dom”: ”>=18.0.0” }
prepublishOnly — runs before every publish automatically: “prepublishOnly”: “npm run build && npm run lint && npm run test”
This saved me from accidentally publishing broken code. Create this in your root to stop unnecessary files from being published: src/ .storybook/ *.stories.tsx *.test.tsx *.test.ts node_modules/ .eslintrc.json .prettierrc tsconfig.json tailwind.config.js tsup.config.ts coverage/ .github/
Without .npmignore, npm would publish your entire project including source code, test files, and config — making your package unnecessarily large. Cartlify uses path aliases to avoid ../../ imports: import { Button } from ‘@primitives/Button’; import { cn } from ‘@utils/cn’; import type { Product } from ‘@types’;
This works fine in development. But when you build for npm, the compiled output still has the alias references — and they break for the end user. The fix is in tsup.config.ts: esbuildOptions(options) { options.alias = { ‘@components’: ’./src/components’, ‘@primitives’: ’./src/primitives’, ‘@hooks’: ’./src/hooks’, ‘@utils’: ’./src/utils’, ‘@types’: ’./src/types’, }; },
This tells tsup to resolve aliases during compilation — the output dist/ files have no aliases, just clean relative paths. Go to npmjs.com → Sign Up Verify your email Enable 2FA — npm now requires this for publishing Login in terminal:
npm login
It opens a browser for 2FA confirmation. Once verified: npm whoami
→ yourusername
Before publishing for real: npm publish —dry-run
This shows exactly what will be uploaded without actually publishing: npm notice 📦 cartlify@1.0.0 npm notice === Tarball Contents === npm notice 2.1kB README.md npm notice 18.4kB dist/index.js npm notice 16.2kB dist/index.mjs npm notice 8.9kB dist/index.d.ts npm notice === Tarball Details === npm notice name: cartlify npm notice version: 1.0.0 npm notice filename: cartlify-1.0.0.tgz npm notice package size: 12.3 kB npm notice unpacked size: 45.6 kB npm notice total files: 4
Check two things: Only dist/ and README.md are listed ✅ No source files, test files, or config files ✅ npm publish —access public
Output: npm notice Publishing to https://registry.npmjs.org/
- cartlify@1.0.0
That’s it. Live at npmjs.com/package/cartlify.
After publishing, add these to your README.md:
They render as clickable badges on GitHub — adds credibility instantly. Cartlify uses a split model: npm install cartlify (free) → Compiled dist/ output → ESM + CJS + TypeScript types → All 4 components usable
Gumroad ($29 one-time) → Full TypeScript source code → Storybook documentation → All hooks, icons, design tokens → README + usage examples → Future updates
The npm package gives developers everything they need to use Cartlify in a project. The Gumroad source lets them read, modify, and fully own the code. This is the same model used by shadcn/ui, Tailwind UI, and most successful component libraries — free to use, paid to own fully.
- tsup is the right tool for libraries.
- prepublishOnly is your safety net.
- Path aliases need special handling. esbuildOptions.alias config.
- Always dry run first. npm publish —dry-run saves you from publishing the wrong files. Run it every time.
- files in package.json matters.
- npm is a credibility signal. npm install cartlify
🔗 npm → npmjs.com/package/cartlify 🔗 GitHub → github.com/thirumalai77/cartlify 🔗 Live Storybook → cartlify.vercel.app 🛒 Full source on Gumroad ($29) → karthiksoftengg.gumroad.com/l/cartlify-react-ui-kit Now that Cartlify is on npm, next steps are: Product Hunt launch Grow npm downloads and Gumroad sales Write more tutorials around the components Will post a 30-day update with real numbers — downloads, views, and sales. If you’ve published an npm package before — what do you wish you’d known? Drop it in the comments. Built by Karthik G S — Senior Frontend Engineer with 10+ years in React, TypeScript, and React Native. Building Cartlify as an indie product alongside a full-time role. Tags: #react #typescript #npm #webdev #javascript #tutorial #showdev #indiehacker