Skip to main content

Compiler features

The IMA.js CLI uses webpack behind the scenes to compile, minify and run the application in dev mode. It comes pre-configured with some options, plugins and loaders, which are described in the following sections.

Server and client bundles

The CLI creates 3 separate bundles (2 in dev mode for performance reasons) with their own configurations. One server bundle (used in express for SSR) and two client bundles - client and, where one targets the es2018 and the other es2022 version of the javascript.

This can be further customized using the disableLegacyBuilt option in ima.config.js.


To make the CLI build both es versions in dev mode, run it with npx ima dev --legacy option.

Keep in mind that hot module replacement (HMR) is configured to work only with the latest es version (manual browser reload is required to see any changes on the legacy version).

Filesystem Cache

The webpack filesystem cache feature is enabled by default to improve consecutive build times in development and production mode.

The CLI automatically generates cache key based on used set of CLI options, which somehow affect the produced output. Not all options affect cache key generation, however you may notice that sometimes the build speeds can behave as if there is no filesystem cache. To see which options affect the cache key generation, take a look at the createCacheKey() function.


Note that each command and bundle maintains it's own set of coexisting cache. To clear the cache, use --clearCache option in build or dev commands.


To bundle JS files we opted to use swc, a Rust-based JavaScript compiler. This decision was based on our results from testing and measuring build times, where we saw 2-3 times the speed benefit (depending on the application size) of using swc over babel compiler.

By default the application compiles both, the application files (sourced from ./app folder) and vendor files (sourced from ./node_modules directory) to make sure that it can run in targeted environments without any issues.

The swc compiler is configured to leverage the power of "env" functionality (preset-env in babel), in combination with core-js to automatically polyfill missing APIs that are used throughout the codebase, that the targeted environment doesn't support.

This configuration can be easily customized using swc option in ima.config.js.


This means that you can write your code using the latest and greatest from the ECMAscript language and the swc makes sure to compile these features down to the latest supported syntax or automatically inject core-js polyfills.


Keep in mind that overuse of these may result in larger JS bundles due to the need to inject more core-js polyfills. Also browser APIs like for example AbortController or fetch are not handled by the core-js and must be included manually. See polyfills in advanced features section.


In dev we use the development version of react library (for better debugging) and react-refresh for HMR. This is switched to production for production builds. By default the compiler is configured to work with automatic JSX runtime, so there's no need to import react library at top of every jsx file. This can be changed to classic in ima.config.js.


From IMA.js v18 we've introduced support for Typescript in your application code. To enable it, simply install typescript dependency and create tsconfig.json file in the root of your project.


For more information and additional tips about TypeScript usage in IMA.js applications, see the TypeScript section.


There's built in support for CSS and LESS preprocessor. Both of these have the same featureset. To use any CSS you have to import the files anywhere in your application. These imports are then combined and extracted to single app.css file.


./app/main.js is a good place to use for global CSS files, since it is an entry point to IMA.js application and these imports will be included at top of the built app.css file.

CSS Modules

Both loaders fully support CSS Modules for files with *.modules.css or *.modules.less postfixes, with local as default scoping.

:global {
:root: {
--bg-color: #fff;

.home {
background: var(--bg-color);
import styles from './home.module.less';

function Home() {
<div className={styles.home}>HomePage</div>


This file is located at ./app/less/globals.less and it is automatically imported on top of every other processed LESS file. It allows you to easily share globals across less files.


Use this file to import other mixins and global variables which are then available in all other *.less files automatically, without the need to import them explicitly.

@import './mixins/*.less';

@global-red: red;
body {
// No direct import of 'globals.less' is needed.
background: @global-red;

Glob less imports

The less-loader uses less-plugin-glob by default in it's configuration. This means that glob imports are fully supported.

@import './mixins/*.less';

/* Non-relative imports are resolved through node resolver. */
@import "@ima/**/atoms/**/*.less";
@import "@ima/**/molecules/**/*.less";
@import "@ima/**/organisms/**/*.less";


IMA.js has built-in support for PostCSS during CSS/LESS compilation.

Out of the box without any additional configuration, it comes pre-configured with following plugins:

  1. postcss-flexbugs-fixes - tries to fix all known flexbox issues.
  2. postcss-preset-env - converts modern CSS back into something the old browsers can understand (back to IE11). It comes with: autoprefixer, stage 3 and custom-properties: false features.

All these features can be easily customized using postcss option in ima.config.js.


All other assets are either inlined as base64 encoded string or copied to the ./build/static/media folder, where default import represents assets's public URL.


Images (bmp, gif, jpeg, png, webp, svg) are automatically inlined if their size is below imageInlineSizeLimit, which can be configured in ima.config.js, with default value of 8Kb. Images exceeding this size limit are copied to the static media folder and import return's their public URL.

To enforce either one of the two modes, you can use ?external or ?inline query parameter in the import path:

// This always converts the image to base64 encoded string and inlines it.
import InlineImage from './image.png?inline';

// This always returns image public URL, no matter it's size
import ImageURL from './image.png?external';

Text files

When you import one of these text files - csv, txt, html, you receive their contents. Similarly to the images, you can enforce getting their public URL by using the ?external query parameter.

// Returns file contents in the default import
import IndexSource from './index.html';

// Returns the file public URL
import IndexURL from './index.html?external';

./app/public folder

Everything in this folder is copied to the ./build/static/public and available through the express static files route (http://localhost:3001/static/public/).


When you built the application bundle, all static assets are additionally compressed using brotli and gzip compression (with .br and .gz extensions respectively). To customize this behavior, take a look at ima.config.js configuration section.


The language files are compile using messageformat library.