Config File

Set up and configure Packem using configuration files

Config File

Packem supports configuration files to customize your build process. While many options can be set via CLI flags, configuration files provide a more maintainable and powerful way to configure complex builds.

Configuration File Discovery

Packem automatically discovers configuration files in the following order:

  1. packem.config.ts
  2. packem.config.js
  3. packem.config.mjs
  4. packem.config.cjs

TypeScript configuration files (packem.config.ts) are recommended as they provide type safety and autocomplete support.

Basic Configuration

TypeScript Configuration

// packem.config.ts
import { defineConfig } from '@visulima/packem/config'

export default defineConfig({
  // Choose your transformer
  transformer: 'esbuild',

  // Rollup configuration
  rollup: {
    output: {
      format: 'esm',
      dir: 'dist'
    },
    sourcemap: true
  }
})

JavaScript Configuration

// packem.config.js
import { defineConfig } from '@visulima/packem/config'

export default defineConfig({
  transformer: 'esbuild',
  rollup: {
    output: {
      format: 'esm',
      dir: 'dist'
    }
  }
})

CommonJS Configuration

// packem.config.cjs
const { defineConfig } = require('@visulima/packem/config')

module.exports = defineConfig({
  transformer: 'esbuild',
  rollup: {
    output: {
      format: 'cjs',
      dir: 'dist'
    }
  }
})

Configuration Schema

The defineConfig function provides full TypeScript support and validation:

export default defineConfig({
  // Entry points (optional - auto-detected from package.json)
  entry?: string | string[] | Record<string, string>

  // Transformer selection
  transformer?: 'esbuild' | 'swc' | 'oxc' | 'sucrase' | 'typescript'

  // Rollup configuration
  rollup?: {
    input?: InputOption
    output?: OutputOptions | OutputOptions[]
    plugins?: Plugin[]
    external?: ExternalOption
    // ... other Rollup options
  }

  // CSS configuration
  css?: {
    extract?: boolean
    modules?: boolean | CssModulesOptions
    loaders?: CssLoader[]
    // ... other CSS options
  }

  // Transformer-specific options
  esbuild?: EsbuildOptions
  swc?: SwcOptions
  oxc?: OxcOptions
  sucrase?: SucraseOptions
  typescript?: TypeScriptOptions

  // Build hooks
  hooks?: {
    'build:before'?: (config) => void | Promise<void>
    'build:after'?: (result) => void | Promise<void>
    // ... other hooks
  }
})

Advanced Configuration

Conditional Configuration

Configure different options based on environment:

export default defineConfig({
  transformer: process.env.NODE_ENV === 'development' ? 'esbuild' : 'swc',

  rollup: {
    output: {
      format: 'esm',
      dir: 'dist',
      sourcemap: process.env.NODE_ENV === 'development'
    },
    plugins: [
      // Development plugins
      ...(process.env.NODE_ENV === 'development' ? [devPlugin()] : []),
      // Production plugins
      ...(process.env.NODE_ENV === 'production' ? [prodPlugin()] : [])
    ]
  }
})

Function-Based Configuration

Use a function for dynamic configuration:

export default defineConfig((env) => {
  const isProduction = env === 'production'

  return {
    transformer: isProduction ? 'swc' : 'esbuild',

    rollup: {
      output: {
        format: 'esm',
        dir: 'dist',
        sourcemap: !isProduction
      }
    },

    css: {
      extract: isProduction,
      modules: true
    }
  }
})

Async Configuration

Load configuration asynchronously:

export default defineConfig(async () => {
  const packageJson = await import('./package.json')

  return {
    transformer: 'esbuild',

    rollup: {
      output: {
        format: packageJson.type === 'module' ? 'esm' : 'cjs',
        dir: 'dist'
      }
    }
  }
})

Multiple Configurations

Export an array of configurations for multiple builds:

export default defineConfig([
  // ESM build
  {
    transformer: 'esbuild',
    rollup: {
      output: {
        format: 'esm',
        file: 'dist/index.mjs'
      }
    }
  },

  // CJS build
  {
    transformer: 'esbuild',
    rollup: {
      output: {
        format: 'cjs',
        file: 'dist/index.cjs'
      }
    }
  },

  // IIFE build for browsers
  {
    transformer: 'esbuild',
    rollup: {
      output: {
        format: 'iife',
        file: 'dist/index.global.js',
        name: 'MyLibrary'
      }
    }
  }
])

Configuration Merging

Packem automatically merges configurations from multiple sources:

  1. Default configuration - Sensible defaults
  2. Configuration file - Your custom configuration
  3. CLI flags - Command-line overrides
// packem.config.ts
export default defineConfig({
  transformer: 'esbuild',
  rollup: {
    output: {
      dir: 'dist'
    }
  }
})
# CLI flags override config file
packem build --transformer swc --outDir build

Environment Variables

Access environment variables in your configuration:

export default defineConfig({
  transformer: process.env.PACKEM_TRANSFORMER || 'esbuild',

  rollup: {
    output: {
      dir: process.env.PACKEM_OUTPUT_DIR || 'dist',
      sourcemap: process.env.NODE_ENV !== 'production'
    }
  },

  // Custom environment variables
  define: {
    'process.env.VERSION': JSON.stringify(process.env.npm_package_version),
    'process.env.BUILD_TIME': JSON.stringify(new Date().toISOString())
  }
})

Loading from .env Files

You can load compile-time environment variables from .env files:

export default defineConfig({
  // Path to the .env file (relative to project root or absolute)
  envFile: '.env',

  // Prefix to filter environment variables (default: 'PACKEM_')
  // Only variables starting with this prefix will be loaded
  envPrefix: 'PACKEM_',

  // ...
})

CLI Options:

You can also specify these via command-line flags:

# Load from .env file
packem build --env-file .env

# Custom prefix
packem build --env-file .env --env-prefix BUILD_

# CLI options override config file options
packem build --env-file .env.production --env-prefix PROD_

How it works:

  1. Variables are loaded from the specified .env file
  2. Only variables matching the envPrefix are included (default: PACKEM_)
  3. Variables are replaced at compile-time in your source code
  4. CLI --env.* variables override .env file variables

Example:

# .env file
PACKEM_API_URL=https://api.example.com
PACKEM_VERSION=1.0.0
OTHER_VAR=ignored  # Not loaded (doesn't match prefix)
// src/index.ts
export const apiUrl = process.env.PACKEM_API_URL;
export const version = process.env.PACKEM_VERSION;

After building, these will be replaced with their actual values at compile-time.

Configuration Validation

Packem validates your configuration and provides helpful error messages:

export default defineConfig({
  transformer: 'invalid', // ❌ TypeScript error

  rollup: {
    output: {
      format: 'unknown' // ❌ TypeScript error
    }
  }
})

Custom Configuration Location

Specify a custom configuration file location:

packem build --config ./configs/packem.config.ts
PACKEM_CONFIG=./configs/packem.config.ts packem build

Configuration Presets

Packem provides built-in presets for common use cases, and you can also create your own reusable configuration presets.

Built-in Presets

React Preset

The React preset configures Babel to handle React JSX transformation. Babel runs before your main transformer (esbuild/SWC/etc.), transforming JSX into JavaScript that the transformer can then process. TypeScript is handled by the transformer via parser plugins, not by Babel.

Quick Setup:

Use the add command to automatically configure React:

npx packem add react

This will automatically set up the React preset and install all necessary dependencies.

Basic Usage:

import { defineConfig } from '@visulima/packem/config'
import transformer from '@visulima/packem/transformer/esbuild'

export default defineConfig({
  preset: 'react',
  transformer,
})

With React Compiler:

import { defineConfig } from '@visulima/packem/config'
import { createReactPreset } from '@visulima/packem/config/preset/react'
import transformer from '@visulima/packem/transformer/esbuild'

export default defineConfig({
  preset: createReactPreset({
    compiler: true
  }),
  transformer,
})

With React Compiler Annotation Mode:

import { defineConfig } from '@visulima/packem/config'
import { createReactPreset } from '@visulima/packem/config/preset/react'
import transformer from '@visulima/packem/transformer/esbuild'

export default defineConfig({
  preset: createReactPreset({
    compiler: {
      compilationMode: 'annotation',
      panicThreshold: 'critical_errors'
    }
  }),
  transformer,
})

Custom Options:

import { defineConfig } from '@visulima/packem/config'
import { createReactPreset } from '@visulima/packem/config/preset/react'
import transformer from '@visulima/packem/transformer/esbuild'

export default defineConfig({
  preset: createReactPreset({
    compiler: true,
    plugins: [],      // Additional Babel plugins
    presets: []       // Additional Babel presets
  }),
  transformer,
})

How it works:

  1. Babel (runs first): Transforms JSX → JS (handles JSX transformation with React Compiler if enabled)
  2. Transformer (esbuild/SWC/etc.): Processes the code and handles TypeScript transformation via parser plugins
  3. Rollup plugins: Can parse the code since it's pure JavaScript

React Preset Options:

  • compiler: Enable React Compiler optimization
    • boolean: Enable with default options
    • object: Configure compiler options
      • compilationMode: "infer" (default) or "annotation" - In annotation mode, only files with "use memo" directive are processed
      • panicThreshold: "critical_errors" (default) or "all_errors"
  • plugins: Additional Babel plugins to include
  • presets: Additional Babel presets to include

Note: The React preset automatically excludes react and react-dom from unused dependency validation, as these are typically peer dependencies. TypeScript is handled by the transformer (esbuild/SWC/etc.) via parser plugins, not by Babel. This matches the approach used by @vitejs/plugin-react.

SolidJS Preset

The SolidJS preset configures Babel to handle SolidJS JSX transformation. Babel runs before your main transformer (esbuild/SWC/etc.), transforming JSX into JavaScript that the transformer can then process. TypeScript is handled by the transformer via parser plugins, not by Babel.

Quick Setup:

Use the add command to automatically configure SolidJS:

npx packem add solid

This will automatically set up the SolidJS preset and install all necessary dependencies.

Basic Usage:

import { defineConfig } from '@visulima/packem/config'
import transformer from '@visulima/packem/transformer/esbuild'

export default defineConfig({
  preset: 'solid',
  transformer,
})

With Custom Solid Options:

import { defineConfig } from '@visulima/packem/config'
import { createSolidPreset } from '@visulima/packem/config/preset/solid'
import transformer from '@visulima/packem/transformer/esbuild'

export default defineConfig({
  preset: createSolidPreset({
    solidOptions: {
      generate: 'ssr',
      hydratable: true,
      moduleName: 'solid-js/web'
    }
  }),
  transformer,
})

With Custom Babel Options:

import { defineConfig } from '@visulima/packem/config'
import { createSolidPreset } from '@visulima/packem/config/preset/solid'
import transformer from '@visulima/packem/transformer/esbuild'

export default defineConfig({
  preset: createSolidPreset({
    solidOptions: {
      generate: 'dom'
    },
    env: {
      targets: 'last 2 years'
    },
    typescript: true
  }),
  transformer,
})

How it works:

  1. Babel (runs first): Transforms JSX → JS (handles JSX transformation with Solid preset)
  2. Transformer (esbuild/SWC/etc.): Processes the code and handles TypeScript transformation via parser plugins
  3. Rollup plugins: Can parse the code since it's pure JavaScript

Solid Preset Options:

  • solidOptions: Configure SolidJS-specific options
    • moduleName: Runtime module name (default: "solid-js/web")
    • generate: Output mode - "dom" (default) or "ssr" for server-side rendering
    • hydratable: Enable hydratable markers (default: false)
    • delegateEvents: Enable automatic event delegation (default: true)
    • wrapConditionals: Enable smart conditional detection (default: true)
    • contextToCustomElements: Set render context on Custom Elements (default: true)
    • builtIns: Array of Component exports to auto-import
  • env: Enable @babel/preset-env (default: true)
    • boolean: Enable with default targets ("last 2 years")
    • object: Configure with custom targets
  • plugins: Additional Babel plugins to include
  • presets: Additional Babel presets to include

Note: The Solid preset automatically excludes solid-js, solid-js/web, and solid-js/store from unused dependency validation, as these are typically peer dependencies. TypeScript is handled by the transformer (esbuild/SWC/etc.) via parser plugins, not by Babel.

Vue Preset

The Vue preset configures Rollup with unplugin-vue to handle Vue 3 Single File Components (SFCs). The plugin processes .vue files and compiles them for Rollup bundling.

Quick Setup:

Use the add command to automatically configure Vue:

npx packem add vue

This will automatically set up the Vue preset and install all necessary dependencies.

Basic Usage:

import { defineConfig } from '@visulima/packem/config'
import transformer from '@visulima/packem/transformer/esbuild'

export default defineConfig({
  preset: 'vue',
  transformer,
})

With Custom Options:

import { defineConfig } from '@visulima/packem/config'
import { createVuePreset } from '@visulima/packem/config/preset/vue'
import transformer from '@visulima/packem/transformer/esbuild'

export default defineConfig({
  preset: createVuePreset({
    pluginOptions: {
      customElement: true,
      include: [/\.vue$/],
      exclude: [/\.vue\.test\.vue$/],
      template: {
        compilerOptions: {
          isCustomElement: (tag) => tag.startsWith('my-')
        }
      }
    }
  }),
  transformer,
})

How it works:

  1. Rollup Plugin (unplugin-vue): Processes .vue files, extracts <script>, <template>, and <style> blocks
  2. Transformer (esbuild/SWC/etc.): Processes the compiled JavaScript and handles TypeScript transformation
  3. CSS Processing: Styles are handled by Packem's CSS processing pipeline

Vue Preset Options:

  • pluginOptions: Configure the Vue plugin options
    • customElement: Enable custom elements mode (default: false)
    • include: Include patterns for Vue files (default: [/\.vue$/])
    • exclude: Exclude patterns for Vue files
    • template: Template compiler options
      • compilerOptions: Vue template compiler options
        • isCustomElement: Function to determine if a tag is a custom element

Note: The Vue preset automatically excludes vue from unused dependency validation, as it's typically a peer dependency.

Svelte Preset

The Svelte preset configures Rollup with rollup-plugin-svelte to handle Svelte components. The plugin processes .svelte files and compiles them for Rollup bundling.

Quick Setup:

Use the add command to automatically configure Svelte:

npx packem add svelte

This will automatically set up the Svelte preset and install all necessary dependencies.

Basic Usage:

import { defineConfig } from '@visulima/packem/config'
import transformer from '@visulima/packem/transformer/esbuild'

export default defineConfig({
  preset: 'svelte',
  transformer,
})

With Custom Options:

import { defineConfig } from '@visulima/packem/config'
import { createSveltePreset } from '@visulima/packem/config/preset/svelte'
import transformer from '@visulima/packem/transformer/esbuild'

export default defineConfig({
  preset: createSveltePreset({
    pluginOptions: {
      compilerOptions: {
        generate: 'ssr',
        hydratable: true,
        customElement: false
      },
      include: [/\.svelte$/],
      exclude: [/\.svelte\.test\.svelte$/],
      preprocess: {
        // Preprocessor options
      }
    }
  }),
  transformer,
})

How it works:

  1. Rollup Plugin (rollup-plugin-svelte): Processes .svelte files, compiles components to JavaScript
  2. Transformer (esbuild/SWC/etc.): Processes the compiled JavaScript and handles TypeScript transformation
  3. CSS Processing: Styles are extracted and handled by Packem's CSS processing pipeline

Svelte Preset Options:

  • pluginOptions: Configure the Svelte plugin options
    • compilerOptions: Svelte compiler options
      • generate: Output mode - "dom" (default) or "ssr" for server-side rendering
      • hydratable: Enable hydratable markers (default: false)
      • customElement: Compile components to custom elements (default: false)
      • dev: Enable dev mode (default: false)
    • include: Include patterns for Svelte files (default: [/\.svelte$/])
    • exclude: Exclude patterns for Svelte files
    • preprocess: Preprocessor options for Svelte components

Note: The Svelte preset automatically excludes svelte from unused dependency validation, as it's typically a peer dependency.

Custom Presets

Create reusable configuration presets:

// configs/presets.ts
export const libraryPreset = {
  transformer: 'esbuild' as const,
  rollup: {
    output: {
      format: 'esm' as const,
      dir: 'dist'
    },
    sourcemap: true
  },
  hooks: {
    'build:before': async () => {
      console.log('Preset hook: Preparing library build')
    }
  }
}

export const cliPreset = {
  transformer: 'esbuild' as const,
  rollup: {
    output: {
      format: 'cjs' as const,
      dir: 'bin'
    }
  }
}
// packem.config.ts
import { defineConfig } from '@visulima/packem/config'
import { libraryPreset } from './configs/presets'

export default defineConfig({
  ...libraryPreset,

  // Override specific options
  rollup: {
    ...libraryPreset.rollup,
    css: {
      mode: 'extract'
    }
  },

  // Hooks from preset are automatically merged
  // You can add your own hooks or override preset hooks
  hooks: {
    'build:after': async () => {
      console.log('User hook: Build completed')
    }
    // Preset's 'build:before' hook is still registered
  }
})

Hooks from presets are automatically merged with your configuration. If you define the same hook in both, your hook will take precedence.

Configuration Composition

Compose configurations from multiple sources:

import { defineConfig } from '@visulima/packem/config'
import { mergeConfig } from '@visulima/packem/utils'
import baseConfig from './base.config'
import cssConfig from './css.config'

export default defineConfig(
  mergeConfig(
    baseConfig,
    cssConfig,
    {
      // Additional overrides
      transformer: 'swc'
    }
  )
)

IDE Support

TypeScript IntelliSense

Get full autocomplete and type checking in your configuration:

import { defineConfig } from '@visulima/packem/config'

export default defineConfig({
  // TypeScript will provide autocomplete here
  transformer: '|', // Shows: 'esbuild' | 'swc' | 'oxc' | 'sucrase' | 'typescript'

  rollup: {
    output: {
      format: '|' // Shows: 'es' | 'cjs' | 'iife' | 'umd' | 'system'
    }
  }
})

Configuration Schema

Import types for better IDE support:

import type { PackemConfig } from '@visulima/packem/config'

const config: PackemConfig = {
  transformer: 'esbuild',
  rollup: {
    output: {
      format: 'esm',
      dir: 'dist'
    }
  }
}

export default defineConfig(config)

Debugging Configuration

Enable debug mode to see the resolved configuration:

# Debug configuration resolution
PACKEM_DEBUG=1 packem build

# Debug specific areas
PACKEM_DEBUG=config,rollup packem build

Or programmatically:

export default defineConfig({
  debug: true, // Enable debug logging

  transformer: 'esbuild',
  // ... rest of config
})

Configuration Examples

React Library

export default defineConfig({
  transformer: 'esbuild',

  esbuild: {
    jsx: 'automatic',
    jsxImportSource: 'react'
  },

  rollup: {
    external: ['react', 'react-dom'],
    output: [
      { format: 'esm', file: 'dist/index.mjs' },
      { format: 'cjs', file: 'dist/index.cjs' }
    ]
  }
})

Node.js CLI Tool

export default defineConfig({
  transformer: 'esbuild',

  rollup: {
    output: {
      format: 'cjs',
      file: 'bin/cli.js',
      banner: '#!/usr/bin/env node'
    }
  }
})

Full-Stack Library

export default defineConfig([
  // Server build
  {
    entry: 'src/server.ts',
    transformer: 'esbuild',
    rollup: {
      output: {
        format: 'cjs',
        file: 'dist/server.js'
      },
      external: ['express', 'mongoose']
    }
  },

  // Client build
  {
    entry: 'src/client.ts',
    transformer: 'esbuild',
    rollup: {
      output: {
        format: 'esm',
        file: 'dist/client.mjs'
      }
    },
    css: {
      extract: true,
      modules: true
    }
  }
])

  • Entry - Configure entry points
  • Output Format - Control output formats
  • Transformers - Choose and configure transformers
  • Hooks - Build lifecycle hooks
Support

Contribute to our work and keep us going

Community is the heart of open source. The success of our packages wouldn't be possible without the incredible contributions of users, testers, and developers who collaborate with us every day.Want to get involved? Here are some tips on how you can make a meaningful impact on our open source projects.

Ready to help us out?

Be sure to check out the package's contribution guidelines first. They'll walk you through the process on how to properly submit an issue or pull request to our repositories.

Submit a pull request

Found something to improve? Fork the repo, make your changes, and open a PR. We review every contribution and provide feedback to help you get merged.

Good first issues

Simple issues suited for people new to open source development, and often a good place to start working on a package.
View good first issues