| | |

Parcel 29 ๐Ÿ“ฆ Deployment โ€” Static hosting & server configuration

โฑ๏ธ Estimated read: 12 min | |

Parcel produces a static bundle. The parcel build command reads the entry fileโ€”usually an index.htmlโ€”and writes the optimized output to the dist directory. The deployment is the process of serving that dist directory from a static host, and the server configuration is the set of rules that tells the host how to serve the files: the build command, the publish directory, and the rewrite rule for a single-page application. The build is the same regardless of the host. The configuration is host-specific, and the two settings that cause the most deployment failures are the publish directory and the public URL.

The publish directory is dist by default. Parcel’s documentation states that the implicit default target has the output folder ~/dist/, and the --dist-dir flag or the targets.*.distDir field in package.json can override it . A host that is configured to publish the project root instead of dist will serve the source files, not the bundle, and the application will fail to load. The public URL is the second setting. Parcel generates absolute paths for the assets by default, and a deployment under a subpathโ€”a GitHub Pages project site at /repo-name/, for exampleโ€”needs the --public-url ./ flag to produce relative paths . Without the flag, the assets point to the domain root, and the subpath deployment loads a blank page.

Key point: The build command is parcel build <entry>, the publish directory is dist, and the public URL is the flag that makes the asset paths work under a subpath. The single-page application needs a rewrite rule that serves index.html for unmatched routes, and the rule is host-specific.


Why the deployment configuration is host-specific

The build output is the same. Parcel writes the bundle to dist regardless of the host. The parcel build command bundles, minifies, and content-hashes the output, and the dist directory contains the index.html, the hashed JavaScript and CSS, and the copied static assets . The host does not change the build; it changes how the build is served.

The publish directory problem. A static host needs to know which directory contains the files to serve. The answer is dist, but the host’s default is often the project root. A Netlify configuration sets publish = "dist", and a Vercel configuration sets distDir: "dist" . A host that is configured to serve the root will serve the source index.html, which references the source files, not the bundle. The error is a blank page or a 404 on the asset paths.

The public URL problem. Parcel’s default asset paths are absolute (/index.hash.js). A deployment at the domain root works because the absolute path resolves to the correct location. A deployment under a subpath (/repo-name/) does not, because /index.hash.js points to the domain root, not the subpath. The --public-url ./ flag produces relative paths (./index.hash.js), which resolve against the current directory and work under any subpath . The flag is the fix for the GitHub Pages and subdirectory deployments.

The SPA rewrite problem. A single-page application has one index.html and a client-side router. A request for /dashboard does not match a file on disk, and the static host returns a 404 before the router can run. The rewrite rule serves index.html for unmatched paths, and the router reads the URL and renders the correct component. The rule is a _redirects file on Netlify and Cloudflare Pages, a rewrites array in vercel.json, and a try_files directive on Nginx .


a. The build configuration and the public URL

The package.json scripts are the build configuration. The build script runs parcel build with the entry file and, if needed, the --public-url flag.

// package.json โ€” the build script for a root deployment
{
  "scripts": {
    "build": "parcel build src/index.html"
  }
}

The root deployment uses the default absolute paths. The dist directory is the publish directory, and the asset paths resolve from the domain root.

// package.json โ€” the build script for a subpath deployment
{
  "scripts": {
    "build": "parcel build src/index.html --public-url ./"
  }
}

The --public-url ./ flag produces relative paths. The dist directory can be deployed to /repo-name/ on GitHub Pages, and the assets resolve against the subpath . The flag is the difference between a working subpath deployment and a blank page.

The source field in package.json is the alternative to passing the entry file in the script. The field tells Parcel where the entry HTML lives, and the parcel build command reads it . The main field inference is the trap: Parcel reads the main field and treats the project as a library if the field is present. A web application should set "targets": { "main": false } to disable the inference, or remove the main field entirely .


b. The host configuration and the SPA rewrite

The host configuration has two parts: the build settings and the rewrite rule. The build settings tell the host how to run the build and where the output is. The rewrite rule tells the host how to serve the SPA routes.

Netlify. The netlify.toml file sets the build command and the publish directory. The [[redirects]] section is the SPA rewrite.

# netlify.toml โ€” the build and rewrite configuration
[build]
  command = "parcel build src/index.html"
  publish = "dist"

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

The publish = "dist" is the directory that Netlify serves. The status = 200 makes the rewrite a rewrite rather than a redirect . The _redirects file in the dist directory is the alternative for a project that does not use netlify.toml.

Vercel. The vercel.json file sets the build and the rewrite. The builds section points to the entry and the distDir. The routes section is the SPA fallback.

// vercel.json โ€” the build and rewrite configuration
{
  "builds": [
    {
      "src": "src/index.html",
      "use": "@vercel/static-build",
      "config": { "distDir": "dist" }
    }
  ],
  "routes": [
    { "handle": "filesystem" },
    { "src": "/.*", "dest": "/index.html" }
  ]
}

The handle: "filesystem" route serves the static files first. The second route is the fallback that serves index.html for the unmatched paths . The distDir is the directory that Vercel serves.

GitHub Pages. The gh-pages package is the deployment tool. The build script uses the --public-url ./ flag, and the deploy script pushes the dist directory to the gh-pages branch.

// package.json โ€” the GitHub Pages scripts
{
  "scripts": {
    "build": "parcel build src/index.html --public-url ./",
    "deploy": "gh-pages -d dist"
  }
}

The --public-url ./ is the flag that makes the subpath deployment work . The gh-pages -d dist command publishes the dist directory.


Complete Example Session

// ============================================
// PART 1: THE BUILD SCRIPT
// ============================================

// package.json
{
  "source": "src/index.html",
  "scripts": {
    "build": "parcel build src/index.html"
  },
  "targets": {
    "main": false
  }
}

// The source field is the entry.
// The targets.main: false disables the main field inference.

// ============================================
// PART 2: THE SUBPATH BUILD
// ============================================

// package.json โ€” the GitHub Pages build
{
  "scripts": {
    "build": "parcel build src/index.html --public-url ./"
  }
}

// The --public-url ./ flag produces relative paths.
// The assets resolve against the subpath.

// ============================================
// PART 3: THE NETLIFY CONFIG
// ============================================

// netlify.toml
[build]
  command = "parcel build src/index.html"
  publish = "dist"

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

// The publish is dist.
// The redirect is the SPA fallback.

// ============================================
// PART 4: THE VERCEL CONFIG
// ============================================

// vercel.json
{
  "builds": [{
    "src": "src/index.html",
    "use": "@vercel/static-build",
    "config": { "distDir": "dist" }
  }],
  "routes": [
    { "handle": "filesystem" },
    { "src": "/.*", "dest": "/index.html" }
  ]
}

// The distDir is dist.
// The routes are the SPA fallback.

// ============================================
// PART 5: THE GITHUB PAGES DEPLOY
// ============================================

// package.json
{
  "scripts": {
    "build": "parcel build src/index.html --public-url ./",
    "deploy": "gh-pages -d dist"
  }
}

// The build produces the relative-path bundle.
// The deploy pushes dist to the gh-pages branch.

// ============================================
// PART 6: THE PUBLIC URL PROBLEM
// ============================================

// A subpath deployment without --public-url ./:
// The asset paths are /index.hash.js.
// The browser requests the domain root, not the subpath.
// The page is blank.

// With --public-url ./:
// The asset paths are ./index.hash.js.
// The browser resolves against the subpath.
// The page loads.

// ============================================
// PART 7: THE PUBLISH DIRECTORY PROBLEM
// ============================================

// A host configured to serve the project root:
// The source index.html is served.
// The source index.html references the source files.
// The bundle is not loaded.

// The fix: set the publish directory to dist.

// ============================================
// PART 8: THE SPA REWRITE
// ============================================

// A request for /dashboard:
// The static host looks for a file.
// The file does not exist.
// The 404 is returned before the router runs.

// The rewrite rule serves index.html for the path.
// The router reads /dashboard and renders the route.

// ============================================
// PART 9: THE VERIFICATION
// ============================================

// 1. Run the build: npm run build
// 2. Inspect dist/: the index.html and the hashed assets
// 3. Deploy the dist/ directory to the host
// 4. Open the root URL
// 5. Navigate to a route and refresh
// 6. Check the network tab for the asset paths

// ============================================
// PART 10: THE CHECKLIST
// ============================================

// [ ] The build command is parcel build.
// [ ] The publish directory is dist.
// [ ] The main field inference is disabled.
// [ ] The --public-url ./ flag is used for a subpath deployment.
// [ ] The SPA rewrite rule is configured on the host.
// [ ] The asset paths resolve correctly after deployment.

The ten parts covered the build script, the subpath build, the Netlify config, the Vercel config, the GitHub Pages deploy, the public URL problem, the publish directory problem, the SPA rewrite, the verification, and the checklist. The through-line is that the build is the same and the host configuration is the variable.


Quick Reference

The build settings

SettingValue
Build commandparcel build src/index.html
Publish directorydist
Subpath flag--public-url ./
Main inference disable"targets": { "main": false }

The host configurations

HostFilePublish settingSPA rewrite
Netlifynetlify.tomlpublish = "dist"[[redirects]]
Vercelvercel.jsondistDir: "dist"routes array
GitHub Pagespackage.jsongh-pages -d dist(not supported)

The public URL

DeploymentFlagAsset path
Domain root(none)/index.hash.js
Subpath--public-url ././index.hash.js

Best Practices

โœ… Do This:

// Use the source field or pass the entry explicitly
"build": "parcel build src/index.html"                           // โœ…
# Use --public-url ./ for a subpath deployment
parcel build src/index.html --public-url ./                      // โœ…
# Set the publish directory to dist
[build] publish = "dist"                                         // โœ…

โŒ Don’t Do This:

# Deploy without the public URL for a subpath
parcel build src/index.html // assets point to the root         // โŒ
// Leave the main field inference enabled
"main": "index.js" // Parcel treats the project as a library     // โŒ
# Configure the host to serve the project root
# The source files are served, not the bundle.                 // โŒ

Common Pitfalls

PitfallWhy It HappensFix
Blank page on a subpathThe asset paths are absoluteUse --public-url ./
The source files are servedThe publish directory is the rootSet publish = "dist"
The build outputs to the wrong pathThe main field is inferredSet targets.main: false
A route returns a 404 on refreshNo SPA rewrite ruleAdd the host-specific rewrite
The images are 404The public URL does not match the deploymentSet the correct --public-url

Real-World Examples

1. The build command

"build": "parcel build src/index.html"

2. The subpath build

"build": "parcel build src/index.html --public-url ./"

3. The Netlify config

[build] publish = "dist"
[[redirects]] from = "/*" to = "/index.html" status = 200

4. The Vercel config

{ "builds": [{ "src": "src/index.html", "use": "@vercel/static-build", "config": { "distDir": "dist" } }] }

5. The GitHub Pages deploy

"deploy": "gh-pages -d dist"

6. The main field disable

"targets": { "main": false }

7. The source field

"source": "src/index.html"

8. The publish directory

dist

9. The public URL

--public-url ./

10. The verification

npm run build && ls dist/

Visual

The Deployment Flow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  FROM SOURCE TO HOST                                          โ”‚
โ”‚                                                              โ”‚
โ”‚  src/index.html                                              โ”‚
โ”‚         โ”‚                                                    โ”‚
โ”‚         โ–ผ                                                    โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                    โ”‚
โ”‚  โ”‚ parcel build        โ”‚  Bundles, minifies, hashes        โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                    โ”‚
โ”‚             โ”‚                                                โ”‚
โ”‚             โ–ผ                                                    โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                    โ”‚
โ”‚  โ”‚ dist/               โ”‚  The publish directory             โ”‚
โ”‚  โ”‚ index.html          โ”‚                                    โ”‚
โ”‚  โ”‚ index.hash.js       โ”‚                                    โ”‚
โ”‚  โ”‚ index.hash.css      โ”‚                                    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                    โ”‚
โ”‚             โ”‚                                                โ”‚
โ”‚             โ–ผ                                                    โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                    โ”‚
โ”‚  โ”‚ Static host         โ”‚  Serves dist/                      โ”‚
โ”‚  โ”‚ + rewrite rule      โ”‚                                    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The Public URL Problem

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  ABSOLUTE VS RELATIVE PATHS                                   โ”‚
โ”‚                                                              โ”‚
โ”‚  Without --public-url ./:                                    โ”‚
โ”‚  /index.hash.js  โ†’ domain root                               โ”‚
โ”‚  Subpath deployment โ†’ 404                                    โ”‚
โ”‚                                                              โ”‚
โ”‚  With --public-url ./:                                       โ”‚
โ”‚  ./index.hash.js โ†’ current directory                         โ”‚
โ”‚  Subpath deployment โ†’ works                                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The SPA Rewrite

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  THE REWRITE SERVES index.html FOR UNMATCHED PATHS            โ”‚
โ”‚                                                              โ”‚
โ”‚  Request /dashboard                                          โ”‚
โ”‚         โ”‚                                                    โ”‚
โ”‚         โ–ผ                                                    โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                    โ”‚
โ”‚  โ”‚ The host checks     โ”‚                                    โ”‚
โ”‚  โ”‚ for a file          โ”‚                                    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                    โ”‚
โ”‚             โ”‚                                                โ”‚
โ”‚             โ–ผ                                                    โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                    โ”‚
โ”‚  โ”‚ No file โ†’ serve     โ”‚                                    โ”‚
โ”‚  โ”‚ index.html          โ”‚                                    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                    โ”‚
โ”‚             โ”‚                                                โ”‚
โ”‚             โ–ผ                                                    โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                    โ”‚
โ”‚  โ”‚ The router reads    โ”‚                                    โ”‚
โ”‚  โ”‚ /dashboard          โ”‚                                    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
Build commandparcel build src/index.html
Publish directorydist
Subpath flag--public-url ./
Main inference disable"targets": { "main": false }
Netlify rewrite[[redirects]] in netlify.toml
Vercel rewriteroutes in vercel.json
GitHub Pages deploygh-pages -d dist

Key takeaways:

  • The build is the same; the host configuration is the variable. Parcel writes the bundle to dist, and the deployment is the process of serving that directory with the host-specific settings. The build command is parcel build, and the publish directory is dist .
  • The public URL is the subpath fix. A deployment at the domain root uses absolute paths. A deployment under a subpath needs --public-url ./ to produce relative paths. Without the flag, the assets point to the domain root and the page is blank .
  • The main field inference is the output path trap. Parcel reads the main field and treats the project as a library if the field is present. A web application should set "targets": { "main": false } or remove the field to ensure the output goes to the correct path .
  • The SPA needs a rewrite rule. A static host returns a 404 for a client-side route before the router can run. The rewrite rule serves index.html for unmatched paths, and the rule is host-specific: _redirects on Netlify, routes on Vercel, try_files on Nginx .
  • The publish directory is the most common misconfiguration. A host that is configured to serve the project root instead of dist will serve the source files, not the bundle. The fix is to set the publish directory to dist in the host’s configuration.

Remember: A Parcel deployment is the dist directory plus the host configuration. The build command is parcel build, the publish directory is dist, and the public URL is the flag that makes a subpath deployment work. The main field inference is the trap that writes the output to the wrong path, and the SPA rewrite is the rule that makes client-side routing work on refresh. The build is the same; the host is the variable. The two settings that cause the most failures are the publish directory and the public URL.

Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!