# Configuring Module Format

Modern SDKs need to balance compatibility with performance. The `moduleFormat` option in the SDK generator allows developers to control whether an SDK is built for CommonJS (CJS), ECMAScript Modules (ESM), or both. This choice impacts bundle size, tree-shaking performance, and compatibility with Node.js and modern bundlers.

## How to configure module format

To configure the module format, update the `typescript` section of your `gen.yaml` file (which is often located in the SDK's `.speakeasy` directory):

```yaml filename="<sdk-root>/.speakeasy/gen.yaml"
typescript:
  # add or modify `moduleFormat`
  moduleFormat: "esm" # or "dual" or "commonjs"
  # other Typescript configuration options...
```

### Supported options

Select one of the supported module formats:

- `"esm"` is the default option for new SDKs. It builds SDKs for ECMAScript Modules. ESM is the modern standard for JavaScript modules, providing optimal tree-shaking and significantly smaller bundles when used with bundlers like Webpack, Rollup, or Vite.
- `"dual"` is the default option for existing SDKs. By building SDKs for both CJS and ESM formats, it offers ESM's superior tree-shaking and bundle optimization while maintaining compatibility with older CJS environments. The slight build time increase is often worth the flexibility and performance benefits.
- `"commonjs"` builds SDKs for CommonJS. CJS is widely supported across Node.js environments, but it's less optimized for modern bundlers and tree-shaking.

## Module format overview

The `moduleFormat` determines the module system targeted during SDK building. It impacts:

- Node.js project compatibility
- Bundler tree-shaking capabilities
- SDK bundle size
- Build performance

### Example outputs

Review the different outputs generated for each module format.

#### CJS

The `commonjs` module format outputs the following:

```javascript filename="example.js"
// CommonJS import in consumer code
const { ApiError } = require("petstore/errors/apierror.js");

// ESM import (interop code included)

```

#### ESM

The `esm` module format outputs the following:

```javascript filename="example.js"
// Native ESM import in consumer code

// ❌ Will not work in CommonJS-only environments
```

#### Dual

The `dual` module format outputs the following:

```javascript filename="example.js"
// ESM import (no interop code)

// CommonJS import (still works seamlessly)
const { ApiError } = require("petstore/errors/apierror.js");
```

## How to decide which format to use

We recommend using ESM (`esm`) if:

- Most modern tooling supports ESM, and building for just one target simplifies the build chain.
- Tree-shaking and bundle size optimization are top priorities.
- The project already uses ESM throughout.
- Leveraging the latest JavaScript features and tooling is important.

We recommend using both (`dual`) if:

- You require support for both modern and legacy environments.
- You need ESM's superior tree-shaking while maintaining CJS compatibility.
- The SDK is used in diverse environments with different module requirements.
- Developer experience and maximum flexibility are priorities.

We recommend using CJS (`commonjs`) if:

- Bundle size optimization is not a critical requirement.
- You require maximum compatibility with legacy systems.

<Callout title="Recommendation" type="info">
For most projects, `esm` is the best choice. Most modern tooling supports ESM, and building for a single target simplifies the build chain while providing optimal tree-shaking and bundle size.
</Callout>

## Additional reading

- [TypeScript configuration options](/docs/gen-reference/ts-config)
- [Lean SDKs with standalone functions](/post/standalone-functions)
