The main entry ships a `production` export condition. Under it the package resolves to a module that exports the same three names with the same types and does nothing. Everything else gets the real module.

| bundler         | behaviour                                                                    |
| --------------- | ---------------------------------------------------------------------------- |
| Vite            | automatic. `resolve.conditions` carries `development\|production` by default |
| webpack, Rspack | automatic. `conditionNames` follows `mode`                                   |
| esbuild         | needs `--conditions=production`                                              |
| Rollup          | needs `exportConditions: ["production"]` on the node-resolve plugin          |
| plain Node      | gets the real module. Importing it in Node is safe and does nothing          |

The plugin and the runtime need no such condition. The runtime is reached only through code the plugin injects, and no production build ever sees that code: under Vite the plugin is not loaded outside the dev server, and under webpack and Rspack it is loaded but refuses to transform anything.

The three bundlers in the top half need nothing from you. For esbuild and Rollup, set the condition once in your build config and the rest works the same way.

**The explicit pattern stays supported** for anyone who wants control instead of automation. It also drops the two calls from the bundle, instead of leaving them pointing at empty functions. Use your own bundler's dev flag: `import.meta.env.DEV` is Vite's, and esbuild, Rollup and Node each spell it differently.

```ts
if (import.meta.env.DEV) {
  const { connectDevtools } = await import("nanostores-devtools");

  connectDevtools();
}
```
