# How to Configure SWC Preset-Env for Targeted Browser Support and Polyfill Management

> Configure SWC preset-env for targeted browser support and polyfill management. Learn static and dynamic polyfill injection using `.swcrc` or Rust Config.

- Repository: [swc/swc](https://github.com/swc-project/swc)
- Tags: how-to-guide
- Published: 2026-06-15

---

**SWC's preset-env automatically injects core-js polyfills based on target browser definitions, supporting both static and dynamic polyfill injection via the `mode` option in `.swcrc` or the Rust `Config` struct.**

SWC's **preset-env** transform replicates Babel's `@babel/preset-env` functionality within the high-performance Rust compiler. According to the `swc-project/swc` source code, the `Config` struct in [`swc_ecma_preset_env/src/lib.rs`](https://github.com/swc-project/swc/blob/main/swc_ecma_preset_env/src/lib.rs) drives the entire flow, determining which browsers to support and how to manage polyfill injection from core-js 2 or 3.

## Defining Browser Targets

The **targets** field in `Config` accepts an `Option<Targets>` that determines which runtime environments require polyfill support. In [`preset_env_base/src/query.rs`](https://github.com/swc-project/swc/blob/main/preset_env_base/src/query.rs), the `targets_to_versions` function parses your input into a `Versions` map.

You have two approaches for target definition:

- **Browserslist query**: Pass a standard string like `"> 0.25%, not dead"` to leverage your existing browserslist configuration.
- **Explicit version map**: Define specific versions per browser, such as `{ "chrome": "92", "ie": "11" }`.

The internal `Version` struct in [`preset_env_base/src/version.rs`](https://github.com/swc-project/swc/blob/main/preset_env_base/src/version.rs) compares these against feature requirements to determine which transforms and polyfills are necessary.

## Selecting Core-JS Versions and Injection Modes

The **core_js** field specifies which core-js release to reference (2 or 3), defaulting to version 3 when omitted. This selection pairs with the **mode** option to control polyfill injection strategy.

The `Mode` enum offers two distinct behaviors:

- **`Mode::Usage`**: SWC scans the AST to detect actually-used features, injecting only the specific core-js modules required by your code and target browsers.
- **`Mode::Entry`**: SWC imports all core-js modules necessary for your target browsers regardless of actual usage, creating a comprehensive polyfill bundle.

Both settings are processed in `EnvConfig::from` (lines 706-735 of [`swc_ecma_preset_env/src/lib.rs`](https://github.com/swc-project/swc/blob/main/swc_ecma_preset_env/src/lib.rs)) and forwarded to the `Polyfills` visitor.

## Fine-Tuning Polyfills with Include and Exclude

SWC provides granular control over polyfill inclusion through two `Vec<FeatureOrModule>` fields:

- **`include`**: Forces specific features or core-js modules into the output regardless of the target check. Accepts items like `"es.object.fromEntries"` or features from `transform_data::Feature`.
- **`exclude`**: Prevents specific polyfills from being injected even if required by the target browsers.

The `FeatureOrModule::split` method separates these into distinct feature and module sets. During `Polyfills::collect`, the system consults these sets—omitting excluded modules while ensuring included ones appear in the final bundle.

## Skipping Specific Transforms

The **skip** field holds a `Vec<Atom>` of transform names to disable (e.g., `"es2015.arrowFunctions"` or `"es2020.nullishCoalescing"`). When constructing the transform pipeline in `transform_internal`, SWC checks this list before adding each pass to the compilation chain. This allows you to preserve specific ES syntax features even when targeting older browsers.

## Advanced Configuration Options

Additional boolean flags in `Config` modify transform behavior:

- **`loose`**: Relaxes spec-compliant transforms (particularly class fields and spread operators) to generate smaller, faster output at the cost of strict adherence to the ECMAScript specification.
- **`debug`**: Outputs the target browser list and injected polyfills to stderr during compilation, helping you verify which optimizations SWC applies.
- **`dynamic_import`**: Controls how dynamic `import()` expressions are handled for older module systems, configured in the `transform_internal` builder at line 49 of [`swc_ecma_preset_env/src/lib.rs`](https://github.com/swc-project/swc/blob/main/swc_ecma_preset_env/src/lib.rs).

## Practical Configuration Examples

### JSON Configuration (.swcrc)

The most common approach uses a `.swcrc` file defining the `presetEnv` object within `jsc.transform`:

```json
{
  "jsc": {
    "target": "es2015",
    "parser": { "syntax": "ecmascript", "jsx": true },
    "transform": {
      "presetEnv": {
        "targets": { "chrome": "92", "ie": "11" },
        "coreJs": 3,
        "mode": "usage",
        "exclude": ["web.url", "es.promise.finally"],
        "include": ["es.object.fromEntries"],
        "skip": ["es2020.nullishCoalescing"],
        "loose": true,
        "debug": true
      }
    }
  },
  "module": { "type": "commonjs" }
}

```

### Command Line Usage

Apply your configuration via the SWC CLI:

```bash
swc src/**/*.js --config-file .swcrc

```

### Programmatic Rust API

For direct integration, construct a `Config` and pass it to `transform_from_env`:

```rust
use swc_ecma_preset_env::{Config, Mode, transform_from_env};
use swc_common::{sync::Lrc, SourceMap, Mark};
use preset_env_base::{query::Targets, version::Version};
use swc_ecma_visit::VisitMutWith;

let config = Config {
    targets: Some(Targets {
        browsers: Some(vec!["> 0.25%, not dead".into()]),
        ..Default::default()
    }),
    core_js: Some(Version { major: 3, minor: 0, patch: 0 }),
    mode: Some(Mode::Usage),
    exclude: vec!["web.url".into()],
    include: vec!["es.object.fromEntries".into()],
    ..Default::default()
};

let env_config = config.into();
let mut pass = transform_from_env(
    Mark::new(),
    None,
    env_config,
    swc_ecma_transforms::assumptions::Assumptions::default(),
);

// Apply pass to your module
module.visit_mut_with(&mut pass);

```

The `transform_from_env` function builds the complete pass pipeline, while `Polyfills::visit_mut_module` and `Polyfills::visit_mut_script` handle the actual AST manipulation to inject the determined core-js imports.

## Summary

- **Target Definition**: Use browserslist queries or explicit version maps in [`preset_env_base/src/query.rs`](https://github.com/swc-project/swc/blob/main/preset_env_base/src/query.rs) to specify supported browsers.
- **Polyfill Control**: Choose between `Mode::Usage` (tree-shaken) and `Mode::Entry` (complete) injection strategies.
- **Granular Tuning**: Leverage `include`, `exclude`, and `skip` to override automatic feature detection.
- **Configuration**: Define settings in `.swcrc` JSON or programmatically via the `Config` struct in [`swc_ecma_preset_env/src/lib.rs`](https://github.com/swc-project/swc/blob/main/swc_ecma_preset_env/src/lib.rs).

## Frequently Asked Questions

### What is the difference between usage mode and entry mode in SWC preset-env?

**Usage mode** analyzes your actual source code to determine which JavaScript features you use, then injects only the specific core-js polyfills required for those features in your target browsers. **Entry mode** ignores your source code and instead imports all core-js modules that might be needed for your target browsers, creating a larger but more comprehensive polyfill bundle. Usage mode typically produces smaller bundles, while entry mode ensures runtime safety regardless of code changes.

### How does SWC determine which browsers to support?

SWC parses the `targets` field through the `targets_to_versions` function in [`preset_env_base/src/query.rs`](https://github.com/swc-project/swc/blob/main/preset_env_base/src/query.rs). This accepts either a browserslist query string (like `"> 0.25%, not dead"`) or a JSON object mapping browser names to version strings (like `{ "chrome": "92" }`). The resulting `Versions` map is compared against feature requirements in [`preset_env_base/src/version.rs`](https://github.com/swc-project/swc/blob/main/preset_env_base/src/version.rs) using the `should_enable` helper to decide when transforms and polyfills are necessary.

### Can I exclude specific polyfills even if my target browsers need them?

Yes. The `exclude` field in the preset-env configuration accepts an array of core-js module names (such as `"web.url"` or `"es.promise.finally"`). During the `Polyfills::collect` phase, SWC checks this exclusion list and omits any matching modules from the injection phase, even if the target browser versions would normally require them. This is useful when you know a particular feature is handled by external dependencies or runtime guarantees.

### What happens when I set `loose: true` in the configuration?

Setting `loose: true` relaxes spec-compliance in favor of smaller output size. According to [`swc_ecma_preset_env/src/lib.rs`](https://github.com/swc-project/swc/blob/main/swc_ecma_preset_env/src/lib.rs), this flag adjusts the assumptions passed to individual transforms, causing them to generate simpler code that behaves slightly differently from strict ECMAScript specifications. For example, class fields may use simple assignment instead of DefineProperty semantics. Use this option when you prioritize bundle size over exact spec compliance and understand the semantic differences.