Skip to content

Repo guidance: Use async import stubs

When dynamically importing code from another package, don't directly import it from your app code.

ts
// App.ts (don't do either of these)
const { other } = await import('other-package')
const { thing } = await import('other-package/lib/file')

Problems:

  • No tree shaking, since you're importing the entire entry point as an object and destructuring it at runtime. This is especially bad when importing the whole package (import('other-package')), since any unused exports or future additions will bloat your bundle size.
  • A deep import (that isn't listed in the exports map) like import('other-package/lib/file') is not a good workaround due to the general problems with deep imports.

Create a local stub file which re-exports only the symbols you need from the dependency, and dynamically import that:

ts
// MyPackageThingLazy.ts
export { thing } from 'other-package'

// App.ts
const { thing } = await import('./MyPackageThingLazy')

Benefits:

  • Your async boundary contains only the needed exports.
  • You control its dependencies explicitly; future additions to other-package do not bloat this chunk.
  • Easier to refactor if the dependency changes layout.

Tips

  • Co-locate stubs near primary usage for discoverability.
  • If multiple lazy imports pull the same symbols, consolidate them into one stub to maximize caching.