Bundle Optimization

📘

This guide is specific to Web SDK 2.0. If you are still using 1.x, you can find documentation here. Contact your Incode Representative for upgrade information and check if you are a candidate for this upgrade.

Full rollout to all clients still TBD.

Reduce your bundle size with tree shaking and code splitting.

Tree Shaking

Each module ships at its own subpath. Import only the subpaths you need — the SDK has no barrel re-exports that pull everything in.

✅ Good: Subpath imports

TypeScript

// Side-effect import: registers <incode-phone>, ships only the Phone UI bundle
import '@incodetech/web/phone';
import '@incodetech/web/phone/styles.css';

// Headless: only the Phone manager and its dependencies
import { createPhoneManager } from '@incodetech/core/phone';

❌ Avoid: importing more modules than you use

TypeScript

// Don't register every module if you only need one — each side-effect
// import bundles its UI. Import only what you mount.
import '@incodetech/web/phone';
import '@incodetech/web/email';
import '@incodetech/web/selfie';
import '@incodetech/web/id';

Code Splitting

Use dynamic imports to register modules on demand.

Lazy register a module

TypeScript

// Module registers itself only when this code runs
async function showPhone() {
  await import('@incodetech/web/phone');
  await import('@incodetech/web/phone/styles.css');
  document.body.insertAdjacentHTML(
    'beforeend',
    '<incode-phone></incode-phone>',
  );
  const phone = document.querySelector('incode-phone');
  phone.config = {
    otpVerification: true,
    otpExpirationInMinutes: 5,
    prefill: false,
  };
  phone.onFinish = () => goToSelfie();
}

Prefetch the next module

Load the next step while the user interacts with the current one:

TypeScript

// Start prefetching as soon as the current step begins
const prefetchSelfie = () => import('@incodetech/web/selfie');

phone.onFinish = () => {
  // Selfie chunk is already in the cache by the time we mount it
  goToSelfie();
};

prefetchSelfie();

<incode-flow> optimization

<incode-flow> handles code splitting internally:

You don't need to manage code-splitting yourself when using <incode-flow>.

Bundle size reference

These numbers are a snapshot of the published @incodetech/web build. Your app's final cost depends on bundler, deduplication with framework dependencies, and which modules you actually import — measure with your bundler's built-in size analyzer for an accurate per-app breakdown.

Per-module entry chunks — what gets pulled in when you side-effect-import a single module:

Subpath Entry chunk Gzipped
@incodetech/web/phone 3.8 KB 1.4 KB
@incodetech/web/email 3.9 KB 1.5 KB
@incodetech/web/selfie 1.7 KB 0.7 KB
@incodetech/web/id 16.0 KB 5.6 KB
@incodetech/web/flow 19.3 KB 4.5 KB
@incodetech/web/consent 2.9 KB 1.2 KB
@incodetech/web/curp-validation 9.1 KB 2.4 KB
@incodetech/web/document-capture 34.4 KB 8.9 KB
@incodetech/web/face-match 65.9 KB 45.5 KB
@incodetech/web/identity-reuse 7.3 KB 1.6 KB
@incodetech/web/redirect-to-mobile 41.4 KB 12.7 KB
@incodetech/web/signature 24.7 KB 7.2 KB
@incodetech/web/electronic-signature 1454.8 KB 470.3 KB
@incodetech/web/workflow 6.2 KB 2.3 KB
@incodetech/web/ekyc, /ekyb 8.2 KB / 7.9 KB 2.6 KB / 2.8 KB
@incodetech/web/geolocation 118.8 KB 32.5 KB

Shared chunks — loaded once and reused across modules. Most modules pull in some subset:

Shared chunk Size Gzipped
vendor-preact (Preact + signals) 37.0 KB 12.4 KB
i18n (translation runtime) 79.1 KB 22.3 KB
icons (shared icon set) 17.4 KB 5.6 KB
useModuleLoader (module-loading runtime) 14.7 KB 4.8 KB
homeScreen 12.1 KB 4.5 KB
verifiedByIncode 13.7 KB 5.6 KB
countries (phone module dependency) 138.5 KB 27.0 KB
AsYouType (phone formatter) 82.6 KB 16.3 KB
tutorial, ID tutorial Lotties, etc. 100–230 KB each 8–40 KB

Notes:

If you need the precise number for your build, your bundler is the source of truth. Run vite build --mode analyze (Vite/Rollup), webpack-bundle-analyzer (Webpack), or esbuild --metafile=meta.json (esbuild) and inspect the resulting report.

Bundler Configuration

Vite

TypeScript

// vite.config.ts
export default {
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          'incode-phone': ['@incodetech/web/phone'],
          'incode-selfie': ['@incodetech/web/selfie'],
        },
      },
    },
  },
};

Webpack

JavaScript

// webpack.config.js
module.exports = {
  optimization: {
    splitChunks: {
      cacheGroups: {
        incode: {
          test: /[\\/]node_modules[\\/]@incodetech/,
          name: 'incode-sdk',
          chunks: 'async',
        },
      },
    },
  },
};

Best Practices

  1. Use subpath imports@incodetech/web/phone, never the package root.
  2. Defer registration until you need the module — wrap the side-effect import in await import('@incodetech/web/<module>') inside a click handler or route entry.
  3. Prefetch the next step — fire the dynamic import for the next module while the user is still completing the current one, so it's cached by the time you mount it.
  4. Analyze your bundle — use whatever size analyzer your bundler ships (webpack-bundle-analyzer, rollup-plugin-visualizer, esbuild --metafile, etc.).