<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/ship-cleaner-packages-without-the-dist-or-src-folder-by-publishing-a-subfolder-to-npm-swoo8d8e1" -->

---
title: Ship cleaner packages (without the ./dist or ./src...
description: A technique is walked through for publishing only the contents of a build folder (like ./dist) to NPM as the package root, avoiding leaking implementation...
canonical: https://daily.dev/posts/ship-cleaner-packages-without-the-dist-or-src-folder-by-publishing-a-subfolder-to-npm-swoo8d8e1
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Ship cleaner packages (without the ./dist or ./src folder) by publishing a subfolder to NPM | daily.dev
og:description: A technique is walked through for publishing only the contents of a build folder (like ./dist) to NPM as the package root, avoiding leaking implementation...
og:url: https://daily.dev/posts/ship-cleaner-packages-without-the-dist-or-src-folder-by-publishing-a-subfolder-to-npm-swoo8d8e1
og:image: https://api.daily.dev/og/posts/sWOO8D8E1.png
og:image:alt: Ship cleaner packages (without the ./dist or ./src folder) by publishing a subfolder to NPM
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Ship cleaner packages (without the ./dist or ./src folder) by publishing a subfolder to NPM

**[Bram.us](https://daily.dev/sources/bram)** · 11 min read · 11 upvotes · 5 comments

## Summary

A technique is walked through for publishing only the contents of a build folder (like ./dist) to NPM as the package root, avoiding leaking implementation details like dist/ or src/ into public import paths. The approach uses npm publish ./dist, which treats that folder as the package root. Since this requires the folder to contain its own package.json, README, and LICENSE, a Node.js postbuild script is provided to copy these files and rewrite package.json paths accordingly. Additional safeguards are shown to prevent accidentally running plain npm publish instead of the dedicated build-and-publish script, plus dry-run and npm pack commands to verify the resulting tarball before publishing for real.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://www.bram.us/2026/09/20/npm-publish-subfolder>

## Questions this post answers

### How do I publish only my dist folder as the root of an npm package instead of the whole project?

Run npm publish ./dist, passing a folder path as the argument. npm then treats that folder as the package root rather than the current working directory, so everything inside dist ends up at the top level of the published tarball. The folder must already contain a package.json, README.md, and LICENSE, since npm publish ignores everything outside the target folder.

_Developers wiring up cleaner npm releases can compare build workflows and gotchas on daily.dev._

### Why does my published npm package break when I copy package.json into dist and publish that folder?

The paths inside package.json still reference the dist/ prefix (like "./dist/index.js"), but once dist becomes the package root during publishing, those prefixed paths no longer resolve correctly. The fix is rewriting main, module, and exports fields to remove the ./dist/ prefix, for example changing "./dist/index.js" to "./index.js", before copying it into dist.

_Anyone debugging broken package.json exports after publishing can track similar packaging fixes on daily.dev._

### How can I stop myself from accidentally running npm publish on the wrong folder when my package build is in dist?

Repurpose the prepublishOnly script as a safeguard that checks for a PUBLISH environment variable and exits with an error if it's missing, then add a dedicated pub script that sets PUBLISH=true, runs the build, and calls npm publish ./dist. Running plain npm publish leaves PUBLISH unset and gets blocked, while npm run pub sets it and completes successfully.

_Package maintainers guarding against publish mistakes can find more workflow safeguards on daily.dev._

## Community discussion

Top comments from developers on daily.dev.

**@allinonetools** · 4 upvotes

> This is a nice npm publishing trick. Keeping `dist/` and `src/` out of the public package paths makes the package feel much cleaner for consumers.

**@jenueldev** · 2 upvotes

> Publishing the build directory as the package root is a clean way to keep consumer imports stable, but the safeguards are what make the pattern production-ready. Adding an unpack-and-import smoke test after npm pack in CI could also catch missing metadata or rewritten paths before the release step.

**@pdfopsdev** · 0 upvotes

> exports map covers most of this without the copy step, but TS on moduleResolution node ignores it so subpath imports lose their types, and a subfolder publish sidesteps that. one gotcha, with --provenance the rewritten package.json has to keep repository.url or npm rejects it against the source repo.

**@agustinbarrientos** · 0 upvotes

> I'd add a tiny consumer project that installs the packed tarball and imports every public entry point. A TypeScript consumer could also check the rewritten declaration paths.

## Similar posts on daily.dev

- [Build and publish a TypeScript package to npm](https://daily.dev/posts/build-and-publish-a-typescript-package-to-npm-x7yihxfvp) · Flavio Copes · 1 upvotes · 1 comments
- [pnpm 11 cuts the npm umbilical cord](https://daily.dev/posts/pnpm-11-cuts-the-npm-umbilical-cord-vhucinbuf) · Bytes by ui.dev · 50 upvotes · 5 comments
- [Create and Publish an NPM Package Automatically](https://daily.dev/posts/create-and-publish-an-npm-package-automatically-myjtzzfom) · Telerik · 1 upvotes · 0 comments
- [Issue \#635: npm to scan packages for malware at publish time — Node Weekly](https://daily.dev/posts/issue-635-npm-to-scan-packages-for-malware-at-publish-time-node-weekly-e1c1muywo) · Node Weekly · 2 upvotes · 0 comments
- [npm Staged Publishing Available, Adding a Human Approval Step Before Packages Go Live](https://daily.dev/posts/npm-staged-publishing-available-adding-a-human-approval-step-before-packages-go-live-ut80yuikj) · InfoQ · 0 upvotes · 0 comments

---

Tags: [#javascript](https://daily.dev/tags/javascript), [#nodejs](https://daily.dev/tags/nodejs), [#npm](https://daily.dev/tags/npm), [#pnpm](https://daily.dev/tags/pnpm)

[View this post on daily.dev](https://daily.dev/posts/ship-cleaner-packages-without-the-dist-or-src-folder-by-publishing-a-subfolder-to-npm-swoo8d8e1)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Ship cleaner packages (without the ./dist or ./src folder) by publishing a subfolder to NPM","url":"https://daily.dev/posts/ship-cleaner-packages-without-the-dist-or-src-folder-by-publishing-a-subfolder-to-npm-swoo8d8e1","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/ship-cleaner-packages-without-the-dist-or-src-folder-by-publishing-a-subfolder-to-npm-swoo8d8e1"},"datePublished":"2026-09-20T17:38:46.005Z","dateModified":"2026-09-20T17:39:11.068Z","description":"A technique is walked through for publishing only the contents of a build folder (like ./dist) to NPM as the package root, avoiding leaking implementation...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/7ce223b8d74d4c864b86441c1093488a?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/7ce223b8d74d4c864b86441c1093488a?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Bram.us","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Bram.us","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/3db2116954be44df873ec24cf0e03d14","url":"https://daily.dev/sources/bram"},"commentCount":5,"discussionUrl":"https://daily.dev/posts/ship-cleaner-packages-without-the-dist-or-src-folder-by-publishing-a-subfolder-to-npm-swoo8d8e1","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":11},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":5}],"keywords":"javascript,nodejs,npm,pnpm","timeRequired":"PT11M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Bram.us","item":"https://daily.dev/sources/bram"},{"@type":"ListItem","position":3,"name":"Ship cleaner packages (without the ./dist or ./src folder) by publishing a subfolder to NPM"}]}
{"@context":"https://schema.org","@type":"WebPage","@id":"https://daily.dev/posts/ship-cleaner-packages-without-the-dist-or-src-folder-by-publishing-a-subfolder-to-npm-swoo8d8e1","comment":[{"@type":"Comment","text":"This is a nice npm publishing trick. Keeping dist/ and src/ out of the public package paths makes the package feel much cleaner for consumers.","datePublished":"2026-09-21T03:19:16.134Z","url":"https://daily.dev/posts/sWOO8D8E1#c-SR1sli0Hg","author":{"@type":"Person","name":"AllInOneTools","url":"https://daily.dev/allinonetools","image":"https://media.daily.dev/image/upload/s--4ZT8QlF0--/f_auto/v1769790028/avatars/avatar_igzBsWloJeaegzm9bQltZ?_a=BAMAMiiu0"},"interactionStatistic":{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":4}},{"@type":"Comment","text":"Publishing the build directory as the package root is a clean way to keep consumer imports stable, but the safeguards are what make the pattern production-ready. Adding an unpack-and-import smoke test after npm pack in CI could also catch missing metadata or rewritten paths before the release step.","datePublished":"2026-09-21T03:35:56.967Z","url":"https://daily.dev/posts/sWOO8D8E1#c-XobdVZKW2","author":{"@type":"Person","name":"Jenuel Oras Ganawed","url":"https://daily.dev/jenueldev","image":"https://media.daily.dev/image/upload/s--BadmiNyj--/f_auto/v1787625077/avatars/avatar_JJtZr4Sjm?_a=BAMAMicg0"},"interactionStatistic":{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":2}},{"@type":"Comment","text":"exports map covers most of this without the copy step, but TS on moduleResolution node ignores it so subpath imports lose their types, and a subfolder publish sidesteps that. one gotcha, with --provenance the rewritten package.json has to keep repository.url or npm rejects it against the source repo.","datePublished":"2026-09-21T18:11:04.021Z","url":"https://daily.dev/posts/sWOO8D8E1#c-vVDWDneVo","author":{"@type":"Person","name":"PDFops","url":"https://daily.dev/pdfopsdev","image":"https://media.daily.dev/image/upload/s---8isRBKc--/f_auto/v1782922291/avatars/avatar_orjMeK8QKaaVZwGq7ScPz?_a=BAMAMicg0"}},{"@type":"Comment","text":"I’d add a tiny consumer project that installs the packed tarball and imports every public entry point. A TypeScript consumer could also check the rewritten declaration paths.","datePublished":"2026-09-23T03:23:54.324Z","url":"https://daily.dev/posts/sWOO8D8E1#c-ZH3ZOHlQV","author":{"@type":"Person","name":"Agustin Barrientos","url":"https://daily.dev/agustinbarrientos","image":"https://media.daily.dev/image/upload/s--5ayxQnqn--/f_auto/v1788281802/avatars/avatar_wQYYVe5Tbj0NJ7C7qPoa8?_a=BAMAMicg0"}}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/ship-cleaner-packages-without-the-dist-or-src-folder-by-publishing-a-subfolder-to-npm-swoo8d8e1#faq","mainEntity":[{"@type":"Question","name":"How do I publish only my dist folder as the root of an npm package instead of the whole project?","acceptedAnswer":{"@type":"Answer","text":"Run npm publish ./dist, passing a folder path as the argument. npm then treats that folder as the package root rather than the current working directory, so everything inside dist ends up at the top level of the published tarball. The folder must already contain a package.json, README.md, and LICENSE, since npm publish ignores everything outside the target folder. Developers wiring up cleaner npm releases can compare build workflows and gotchas on daily.dev."}},{"@type":"Question","name":"Why does my published npm package break when I copy package.json into dist and publish that folder?","acceptedAnswer":{"@type":"Answer","text":"The paths inside package.json still reference the dist/ prefix (like \"./dist/index.js\"), but once dist becomes the package root during publishing, those prefixed paths no longer resolve correctly. The fix is rewriting main, module, and exports fields to remove the ./dist/ prefix, for example changing \"./dist/index.js\" to \"./index.js\", before copying it into dist. Anyone debugging broken package.json exports after publishing can track similar packaging fixes on daily.dev."}},{"@type":"Question","name":"How can I stop myself from accidentally running npm publish on the wrong folder when my package build is in dist?","acceptedAnswer":{"@type":"Answer","text":"Repurpose the prepublishOnly script as a safeguard that checks for a PUBLISH environment variable and exits with an error if it's missing, then add a dedicated pub script that sets PUBLISH=true, runs the build, and calls npm publish ./dist. Running plain npm publish leaves PUBLISH unset and gets blocked, while npm run pub sets it and completes successfully. Package maintainers guarding against publish mistakes can find more workflow safeguards on daily.dev."}}]}
```

