Skip to content

Parcel Cheatsheet

Parcel - The Zero Configuration Build Tool

Parcel is a web application bundler, differentiated by its developer experience. It offers blazing fast performance utilizing multicore processing, and requires zero configuration.

Table of Contents

Installation

Global Installation

# Install Parcel globally
npm install -g parcel

# Or with Yarn
yarn global add parcel

# Check version
parcel --version
# Initialize npm project
npm init -y

# Install Parcel as dev dependency
npm install --save-dev parcel

# Or with Yarn
yarn add --dev parcel

Package.json Scripts

{
  "scripts": {
    "start": "parcel src/index.html",
    "dev": "parcel src/index.html --open",
    "build": "parcel build src/index.html",
    "clean": "rm -rf dist .parcel-cache"
  }
}

Getting Started

Basic HTML Entry Point

<!-- src/index.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>My Parcel App</title>
</head>
<body>
  <div id="app"></div>
  <script type="module" src="./index.js"></script>
</body>
</html>

Basic JavaScript Entry

// src/index.js
import './styles.css';

console.log('Hello from Parcel!');

// Import and use a module
import { greet } from './utils';
greet('World');

Basic CSS

/* src/styles.css */
body {
  font-family: Arial, sans-serif;
  margin: 0;
  padding: 20px;
  background-color: #f0f0f0;
}

.container {
  max-width: 800px;
  margin: 0 auto;
  background: white;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}

Run Development Server

# Start development server
npm start

# Or with specific port
parcel src/index.html --port 3000

# Open browser automatically
parcel src/index.html --open

Project Structure

Typical Parcel Project

my-parcel-app/
├── src/
   ├── index.html
   ├── index.js
   ├── styles.css
   ├── components/
      ├── Header.js
      └── Footer.js
   ├── assets/
      ├── images/
      └── fonts/
   └── utils/
       └── helpers.js
├── dist/           # Build output
├── .parcel-cache/  # Parcel cache
├── package.json
└── .gitignore

Multiple Entry Points

{
  "scripts": {
    "start": "parcel src/*.html",
    "build": "parcel build src/*.html"
  }
}

Library Project Structure

my-library/
├── src/
   ├── index.js
   ├── components/
   └── utils/
├── package.json
└── .parcelrc

Development Server

Basic Development Server

# Start dev server
parcel src/index.html

# Custom port
parcel src/index.html --port 8080

# Custom host
parcel src/index.html --host 0.0.0.0

# HTTPS
parcel src/index.html --https

# Open browser
parcel src/index.html --open

Development Server Options

# Disable HMR
parcel src/index.html --no-hmr

# Disable source maps
parcel src/index.html --no-source-maps

# Custom dist directory
parcel src/index.html --dist-dir build

# Watch additional files
parcel src/index.html --watch-dir src/data

Proxy Configuration

// package.json
{
  "scripts": {
    "start": "parcel src/index.html --port 3000"
  },
  "@parcel/resolver-default": {
    "packageExports": true
  }
}

Building for Production

Basic Production Build

# Build for production
parcel build src/index.html

# Build with custom output directory
parcel build src/index.html --dist-dir build

# Build without source maps
parcel build src/index.html --no-source-maps

# Build without optimization
parcel build src/index.html --no-optimize

Build Configuration

{
  "scripts": {
    "build": "parcel build src/index.html",
    "build:analyze": "parcel build src/index.html --reporter @parcel/reporter-bundle-analyzer",
    "build:stats": "parcel build src/index.html --reporter @parcel/reporter-bundle-buddy"
  }
}

Production Optimizations

# Enable scope hoisting
parcel build src/index.html --experimental-scope-hoisting

# Custom public URL
parcel build src/index.html --public-url /my-app/

# Detailed bundle report
parcel build src/index.html --detailed-report

Asset Types

JavaScript and TypeScript

// ES6 modules
import { helper } from './utils/helper.js';

// CommonJS
const lodash = require('lodash');

// Dynamic imports
const module = await import('./dynamic-module.js');

// TypeScript
import { Component } from './Component.ts';

CSS and Preprocessors

/* CSS imports */
@import './normalize.css';
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap');

/* CSS modules */
.container {
  composes: flex from './layout.css';
  background: var(--primary-color);
}
// SCSS
@import './variables';

.component {
  background: $primary-color;

  &:hover {
    background: darken($primary-color, 10%);
  }
}
// Less
@import './variables.less';

.component {
  background: @primary-color;

  &:hover {
    background: darken(@primary-color, 10%);
  }
}

Images and Assets

// Import images
import logo from './assets/logo.png';
import icon from './assets/icon.svg';

// Use in HTML
document.getElementById('logo').src = logo;

// CSS background images
.hero {
  background-image: url('./assets/hero.jpg');
}

Fonts

/* Font imports */
@font-face {
  font-family: 'CustomFont';
  src: url('./assets/fonts/custom-font.woff2') format('woff2'),
       url('./assets/fonts/custom-font.woff') format('woff');
}

body {
  font-family: 'CustomFont', sans-serif;
}

JSON and Data Files

// Import JSON
import data from './data.json';
import config from './config.json';

// Import text files
import template from './template.html';
import shader from './shader.glsl';

Transformations

Babel Configuration

// .babelrc
{
  "presets": [
    ["@babel/preset-env", {
      "targets": {
        "browsers": ["> 1%", "last 2 versions"]
      }
    }],
    "@babel/preset-react"
  ],
  "plugins": [
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-syntax-dynamic-import"
  ]
}

PostCSS Configuration

// .postcssrc
{
  "plugins": {
    "autoprefixer": {
      "grid": true
    },
    "cssnano": {
      "preset": "default"
    }
  }
}

TypeScript Configuration

// tsconfig.json
{
  "compilerOptions": {
    "target": "es2018",
    "module": "esnext",
    "lib": ["dom", "dom.iterable", "es6"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": [
    "src"
  ]
}

React Configuration

// React component
import React from 'react';
import './Component.css';

export default function Component({ title }) {
  return (
    <div className="component">
      <h1>{title}</h1>
    </div>
  );
}

Vue Configuration

<!-- Vue component -->
<template>
  <div class="component">
    <h1>{{ title }}</h1>
  </div>
</template>

<script>
export default {
  props: ['title']
}
</script>

<style scoped>
.component {
  padding: 20px;
}
</style>

Code Splitting

Dynamic Imports

// Dynamic import for code splitting
async function loadModule() {
  const { default: Module } = await import('./heavy-module.js');
  return new Module();
}

// React lazy loading
import React, { Suspense } from 'react';

const LazyComponent = React.lazy(() => import('./LazyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  );
}

Bundle Splitting

// Vendor bundle splitting
import React from 'react';
import ReactDOM from 'react-dom';
import lodash from 'lodash';

// App code
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

Route-based Splitting

// React Router with code splitting
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { Suspense, lazy } from 'react';

const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Contact = lazy(() => import('./pages/Contact'));

function App() {
  return (
    <Router>
      <Suspense fallback={<div>Loading...</div>}>
        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/about" component={About} />
          <Route path="/contact" component={Contact} />
        </Switch>
      </Suspense>
    </Router>
  );
}

Hot Module Replacement

HMR in JavaScript

// Enable HMR for a module
if (module.hot) {
  module.hot.accept('./component.js', function() {
    // Re-render component
    render();
  });
}

// HMR with state preservation
if (module.hot) {
  module.hot.accept();

  if (module.hot.data) {
    // Restore state
    restoreState(module.hot.data.state);
  }

  module.hot.dispose((data) => {
    // Save state
    data.state = getCurrentState();
  });
}

React HMR

// React Fast Refresh (automatic with Parcel)
import React from 'react';

function App() {
  const [count, setCount] = React.useState(0);

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
}

export default App;

CSS HMR

/* CSS changes are automatically hot reloaded */
.component {
  background: blue; /* Change this and see instant update */
  padding: 20px;
  border-radius: 8px;
}

Environment Variables

Using Environment Variables

// Access environment variables
const apiUrl = process.env.API_URL || 'http://localhost:3000';
const isDevelopment = process.env.NODE_ENV === 'development';

console.log('API URL:', apiUrl);
console.log('Development mode:', isDevelopment);

.env Files

# .env
API_URL=https://api.example.com
FEATURE_FLAG=true
DEBUG=false
# .env.development
API_URL=http://localhost:3000
DEBUG=true
# .env.production
API_URL=https://api.production.com
DEBUG=false

Environment-specific Builds

{
  "scripts": {
    "start": "NODE_ENV=development parcel src/index.html",
    "build": "NODE_ENV=production parcel build src/index.html",
    "build:staging": "NODE_ENV=staging parcel build src/index.html"
  }
}

Plugins

Installing Plugins

# Install common plugins
npm install --save-dev @parcel/transformer-sass
npm install --save-dev @parcel/transformer-less
npm install --save-dev @parcel/transformer-stylus
npm install --save-dev @parcel/transformer-typescript

Plugin Configuration

// .parcelrc
{
  "extends": "@parcel/config-default",
  "transformers": {
    "*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"],
    "*.scss": ["@parcel/transformer-sass"]
  },
  "reporters": ["@parcel/reporter-dev-server", "@parcel/reporter-bundle-analyzer"]
}

Custom Plugins

// parcel-plugin-custom.js
const { Transformer } = require('@parcel/plugin');

module.exports = new Transformer({
  async transform({ asset }) {
    const code = await asset.getCode();

    // Transform the code
    const transformedCode = customTransform(code);

    asset.setCode(transformedCode);
    return [asset];
  }
});
# Bundle analyzer
npm install --save-dev @parcel/reporter-bundle-analyzer

# Service worker
npm install --save-dev parcel-plugin-sw-precache

# Compression
npm install --save-dev parcel-plugin-compress

# Clean dist
npm install --save-dev parcel-plugin-clean-dist

Configuration

Basic .parcelrc

{
  "extends": "@parcel/config-default",
  "transformers": {
    "*.{js,mjs,jsm,jsx,es6,cjs,ts,tsx}": [
      "@parcel/transformer-js",
      "@parcel/transformer-react-refresh-wrap"
    ]
  }
}

Advanced Configuration

{
  "extends": "@parcel/config-default",
  "resolvers": ["@parcel/resolver-default"],
  "transformers": {
    "*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"],
    "*.{js,jsx}": ["@parcel/transformer-js"],
    "*.{css,scss,sass}": ["@parcel/transformer-sass", "@parcel/transformer-css"],
    "*.{html,htm}": ["@parcel/transformer-html"]
  },
  "bundler": "@parcel/bundler-default",
  "namers": ["@parcel/namer-default"],
  "runtimes": ["@parcel/runtime-js", "@parcel/runtime-browser-hmr"],
  "optimizers": {
    "*.{js,mjs,jsm,jsx,ts,tsx}": ["@parcel/optimizer-terser"],
    "*.{css,scss,sass}": ["@parcel/optimizer-css"]
  },
  "packagers": {
    "*.html": "@parcel/packager-html",
    "*.{js,mjs,jsm,jsx,ts,tsx}": "@parcel/packager-js",
    "*.{css,scss,sass}": "@parcel/packager-css"
  },
  "compressors": {
    "*": ["@parcel/compressor-gzip"]
  },
  "reporters": ["@parcel/reporter-dev-server"]
}

Target Configuration

// package.json
{
  "targets": {
    "default": {
      "distDir": "dist"
    },
    "modern": {
      "engines": {
        "browsers": "Chrome 80"
      }
    },
    "legacy": {
      "engines": {
        "browsers": "> 1%"
      }
    }
  }
}

Optimization

Production Optimizations

# Build with optimizations
parcel build src/index.html

# Disable optimizations
parcel build src/index.html --no-optimize

# Enable experimental optimizations
parcel build src/index.html --experimental-scope-hoisting

Bundle Size Optimization

// Tree shaking (automatic with ES modules)
import { debounce } from 'lodash-es';

// Avoid importing entire libraries
import debounce from 'lodash/debounce';

// Use dynamic imports for large dependencies
async function loadChart() {
  const { Chart } = await import('chart.js');
  return Chart;
}

Image Optimization

<!-- Automatic image optimization -->
<img src="./image.jpg" alt="Optimized image" />

<!-- WebP support -->
<picture>
  <source srcset="./image.webp" type="image/webp">
  <img src="./image.jpg" alt="Image with WebP fallback">
</picture>

CSS Optimization

/* Automatic CSS optimization */
.component {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

/* PostCSS plugins handle vendor prefixes */
.modern-feature {
  backdrop-filter: blur(10px);
}

Targets

Browser Targets

// package.json
{
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead"
  ]
}

Node.js Target

{
  "targets": {
    "node": {
      "context": "node",
      "engines": {
        "node": ">= 12"
      }
    }
  }
}

Library Target

{
  "targets": {
    "library": {
      "source": "src/index.js",
      "distDir": "lib"
    }
  }
}

Multiple Targets

{
  "targets": {
    "modern": {
      "engines": {
        "browsers": "Chrome 80"
      },
      "distDir": "dist/modern"
    },
    "legacy": {
      "engines": {
        "browsers": "> 1%"
      },
      "distDir": "dist/legacy"
    }
  }
}

Caching

Build Caching

# Cache location
.parcel-cache/

# Clear cache
rm -rf .parcel-cache

# Disable cache
parcel build src/index.html --no-cache

HTTP Caching

// Automatic content hashing for long-term caching
// Output: main.a1b2c3d4.js

// Service worker for caching
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js');
}

Cache Configuration

{
  "cacheDir": ".cache",
  "cache": true
}

Source Maps

Source Map Configuration

# Enable source maps (default in development)
parcel src/index.html

# Disable source maps
parcel src/index.html --no-source-maps

# Production source maps
parcel build src/index.html --source-maps

Source Map Types

// .parcelrc
{
  "extends": "@parcel/config-default",
  "optimizers": {
    "*.js": {
      "sourceMap": {
        "inline": false,
        "inlineSources": true
      }
    }
  }
}

Bundle Analysis

Bundle Analyzer

# Install bundle analyzer
npm install --save-dev @parcel/reporter-bundle-analyzer

# Build with analyzer
parcel build src/index.html --reporter @parcel/reporter-bundle-analyzer

Bundle Buddy

# Install bundle buddy
npm install --save-dev @parcel/reporter-bundle-buddy

# Generate bundle buddy report
parcel build src/index.html --reporter @parcel/reporter-bundle-buddy

Custom Analysis

// analyze-bundle.js
const fs = require('fs');
const path = require('path');

function analyzeBundles() {
  const distDir = path.join(__dirname, 'dist');
  const files = fs.readdirSync(distDir);

  files.forEach(file => {
    const filePath = path.join(distDir, file);
    const stats = fs.statSync(filePath);
    console.log(`${file}: ${(stats.size / 1024).toFixed(2)} KB`);
  });
}

analyzeBundles();

Migration

From Webpack

// webpack.config.js (remove this file)
// Parcel handles most webpack functionality automatically

// Update package.json scripts
{
  "scripts": {
    "start": "parcel src/index.html",
    "build": "parcel build src/index.html"
  }
}

From Create React App

# Remove react-scripts
npm uninstall react-scripts

# Install Parcel
npm install --save-dev parcel

# Update scripts
{
  "scripts": {
    "start": "parcel public/index.html",
    "build": "parcel build public/index.html"
  }
}

From Rollup

// Remove rollup.config.js
// Update package.json

{
  "source": "src/index.js",
  "main": "dist/index.js",
  "scripts": {
    "build": "parcel build"
  }
}

Migration Checklist

  • Remove old bundler configuration files
  • Update package.json scripts
  • Install Parcel and remove old bundler
  • Update import paths if needed
  • Configure .parcelrc if custom transformations needed
  • Test development and production builds
  • Update CI/CD pipelines

Best Practices

Project Organization

# Recommended structure
src/
├── index.html          # Entry point
├── index.js           # Main JavaScript
├── styles/            # Global styles
├── components/        # Reusable components
├── pages/            # Page components
├── utils/            # Utility functions
├── assets/           # Static assets
└── types/            # TypeScript types

Performance Best Practices

  • Use dynamic imports for code splitting
  • Optimize images with appropriate formats
  • Minimize bundle size with tree shaking
  • Enable compression for production builds
  • Use content hashing for caching

Development Best Practices

  • Use HMR for faster development
  • Enable source maps for debugging
  • Configure linting with ESLint and Prettier
  • Use TypeScript for better development experience
  • Set up testing with Jest or other frameworks

Production Best Practices

  • Optimize for target browsers with browserslist
  • Enable all optimizations in production builds
  • Use environment variables for configuration
  • Monitor bundle size with analysis tools
  • Test builds before deployment

Summary

Parcel is a zero-configuration build tool that provides an excellent developer experience with minimal setup. Key features include:

  • Zero Configuration: Works out of the box for most projects
  • Fast Builds: Utilizes multicore processing and caching
  • Hot Module Replacement: Instant updates during development
  • Asset Handling: Supports all common file types
  • Code Splitting: Automatic and manual code splitting
  • Tree Shaking: Removes unused code automatically
  • Source Maps: Built-in source map support
  • Optimization: Automatic production optimizations

By leveraging Parcel's simplicity and power, you can focus on building your application rather than configuring build tools, while still having the flexibility to customize when needed.