Vite

The Vite build setup is the culmination of several attempts to dual publish ESM and CJS for TanStack projects, while preserving compatibility with all Typescript module resolution options.

Do I Need This?

ES Modules (ESM) is the standard for writing JavaScript modules. However, due to the historical dependency on CommonJS (CJS), many ecosystem tools and projects were initially incompatible with ESM. It is becoming exceedingly rare for this to be the case, and I would urge you to consider whether it is necessary to distribute CJS code at all. Sindre Sorhus has a good summary on this issue here.

Setup

The build config is quite opinionated, as it is designed to work with our internal libraries. If you follow the below instructions, it may work for your library too!

package.json

  • Ensure "type": "module" is set.
  • Ensure you have Vite installed. Installing Publint is also recommended.
  • Change your build script to "build": "vite build && publint --strict"
  • Ensure you have an "exports" field. We use this, but you might have different requirements:
json
{
  "exports": {
    ".": {
      "import": {
        "types": "./dist/esm/index.d.ts",
        "default": "./dist/esm/index.js"
      },
      "require": {
        "types": "./dist/cjs/index.d.cts",
        "default": "./dist/cjs/index.cjs"
      }
    },
    "./package.json": "./package.json"
  }
}
{
  "exports": {
    ".": {
      "import": {
        "types": "./dist/esm/index.d.ts",
        "default": "./dist/esm/index.js"
      },
      "require": {
        "types": "./dist/cjs/index.d.cts",
        "default": "./dist/cjs/index.cjs"
      }
    },
    "./package.json": "./package.json"
  }
}

tsconfig.json

  • Ensure your "include" field includes "vite.config.ts".
  • Set "moduleResolution" to "bundler".

vite.config.ts

  • Import mergeConfig and tanstackViteConfig.
  • Merge your custom config first, followed by tanstackViteConfig.
  • Please avoid modifying build in your custom config.
  • See an example below:
ts
import { defineConfig, mergeConfig } from 'vite'
import { tanstackViteConfig } from '@tanstack/config/vite'

const config = defineConfig({
  // Framework plugins, vitest config, etc.
})

export default mergeConfig(
  config,
  tanstackViteConfig({
    entry: './src/index.ts',
    srcDir: './src',
  }),
)
import { defineConfig, mergeConfig } from 'vite'
import { tanstackViteConfig } from '@tanstack/config/vite'

const config = defineConfig({
  // Framework plugins, vitest config, etc.
})

export default mergeConfig(
  config,
  tanstackViteConfig({
    entry: './src/index.ts',
    srcDir: './src',
  }),
)

Frameworks

While this config will work with most frameworks with a Vite adapter, it doesn't mean you should use it for all frameworks, as many have their own build tools which are optimised for their ecosystem. When a framework-specific build tool exists, this should be preferred.

FrameworkRecommendation
Angularng-packagr (official tool)
React@tanstack/config (only if you need dual ESM/CJS)
Solidtsc (preserves JSX, necessary for SSR)
Svelte@sveltejs/package (official tool)
Vue@tanstack/config (only if you need dual ESM/CJS)
Subscribe to Bytes

Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.

Bytes

No spam. Unsubscribe at any time.