Develop and build your client-server projects, powered by typescript and webpack.
npm install --save-dev @gravity-ui/app-builder
@gravity-ui/app-builder
provides CLI (npx app-builder
). You can view available commands with the --help
flag.
npx app-builder dev # to view supported options add the --help flag.
npx app-builder build # to view supported options add the --help flag.
You can use any of these files:
- app-builder.config.ts
- app-builder.config.js
- app-builder.config.json
- app-builder property in your package.json
You can also specify a custom filename using the --config
flag
import {defineConfig} from '@gravity-ui/app-builder';
export default defineConfig({
client: {
// client settings
},
server: {
// server settings
},
});
If the config needs to be conditionally determined, it can export a function instead:
import {defineConfig} from '@gravity-ui/app-builder';
export default defineConfig(
async function (
/** @type dev | build */
command,
/**
* values specified with --env flag
*
* @type {[k in string]: string}
*
* @example
* With follow command:
* app-build dev --env=path.to.member1=value1 --env=path.to.member2=value2
* you get:
* env = {path: {to: {member1: 'value1', member2: 'value2'}}}
*/
env,
) {
return {
verbose: command === 'dev',
client: {
// client settings
},
server: {
// server settings
},
};
},
);
export default config;
{
"app-builder": {
"client": {
// client settings
},
"server": {
// server settings
}
},
"scripts": {
"dev": "app-builder dev",
"build": "app-builder build"
}
}
target
(client | server
) — select compilation unit.verbose
(boolean
) - turn on verbose output.
app-builder
automatically injects environment variables during the build process that are available in your application code:
-
process.env.PUBLIC_PATH
— automatically set to the resolved public path value (including CDN URLs if configured). This allows your application code to dynamically access the correct resource URLs at runtime.// In your application code, you can access: const publicPath = process.env.PUBLIC_PATH; // e.g., "https://cdn.example.com/build/" or "/build/" // Useful for dynamically loading assets or configuring Module Federation const assetUrl = `${process.env.PUBLIC_PATH}images/logo.png`;
Note: On the server side,
process.env.PUBLIC_PATH
is only available when using SWC compiler (compiler: 'swc'
). With TypeScript compiler, this variable is not injected. -
process.env.NODE_ENV
— current environment ('development'
|'production'
) -
process.env.IS_SSR
— boolean flag indicating if code is running in SSR context
app-builder
compiles server with typescript.
Default folder for server code is src/server
. There is must be file tsconfig.json
{
"compilerOptions": {
"outDir": "../../dist/server"
}
}
and index.ts
- server entrypoint.
outDir
- must be configured to place compiled files to {rootDir}/dist/server
.
The server is started with the command node {rootDir}/dist/server/index.js
.
All server settings are used only in dev mode:
port
(number | true
) — specify port that server listens. The port will be used to pass through requests from the client to the server. If set totrue
, the port will be selected automatically. The server is started with the commandAPP_PORT=${port} node dist/server/index.js --port ${port}
.watch
(string[]
) — by defaultapp-builder
monitors onlysrc/server
directory. If you need to watch other directories, specify them here.watchThrottle
(number
) — use to add an extra throttle, or delay restarting.inspect/inspectBrk
(number | true
) — listen for a debugging client on specified port. If specifiedtrue
, try to listen on9229
.compiler
('typescript' | 'swc'
) — choose TypeScript compiler for server code compilation. Default is'typescript'
. Set to'swc'
for faster compilation with SWC.
app-builder
bundles client with webpack. Client code must be in src/ui
folder.
src/ui/entries
- each file in this folder is used as entrypoint. dist/public/build
is output directory for bundles.
All paths must be specified relative rootDir
of the project.
modules
(string[]
) — Tell webpack what directories should be searched when resolving modules.modules
automatically populates withbaseUrl
fromsrc/ui/tsconfig.json
.alias
(Record<string, string>
) — Create aliases to import or require certain modules more easily, more
With this {rootDir}/src/ui/tsconfig.json
:
{
"compilerOptions": {
"baseDir": ".",
"paths": {
"~units": ["units/*"]
}
}
}
modules
will contain ["{rootDir}/src"]
and aliases - {"~units": ["{rootDir}/src/units"]}
;
includes
(string[]
) — additional compilation paths. Example:includes: ['node_modules/my-lib', 'src/shared']
images
(string[]
) — Additional paths for images. Example:images: ['node_modules/my-lib/img']
icons
(string[]
) — Additional paths for svg icons. By default, all svgs with paths includingicons/
will be processed. Example:icons: [node_modules/@fortawesome/fontawesome-pro/svgs]
publicPathPrefix
(string
) — publicPath prefix, will be added to/build/
publicPath
(string
) — publicPath for bundler, this option has higher priority than publicPathPrefixoutputPath
(string
) — Build directory for output, default:dist/public/build
anddist/ssr
- for SSRassetsManifestFile
(string
) — File name for assets manifest, default:assets-manifest.json
symlinks
(boolean
) — Follow symbolic links while looking for a file. moreexternals
— specify dependencies that shouldn't be resolved by webpack, but should become dependencies of the resulting bundle. morenode
— include polyfills or mocks for various node stuff. morefallback
— Redirect module requests when normal resolving fails. morepolyfill
— allow enable Node.jsprocess
object polyfill.hiddenSourceMap
(boolean=true
) - iffalse
- source maps will be generated for prod buildsdisableSourceMapGeneration
(boolean
) — disable sourcemap generation;definitions
— add additional options to DefinePlugin. morenewJsxTransform
(boolean=true
) — use new JSX Transform.svgr
(SvgrConfig
) — svgr plugin options. moreentry
(string | string[] | Record<string, string | string[]>
) — entry for bundler, overrides entry which is generated from entries directoryentryFilter
(string[]
) — filter used entrypoints.excludeFromClean
(string[]
) — do not clean provided paths before build.forkTsCheker
(false | ForkTsCheckerWebpackPluginOptions
) - config for ForkTsCheckerWebpackPlugin more. Iffalse
, ForkTsCheckerWebpackPlugin will be disabled.cache
(boolean | FileCacheOptions | MemoryCacheOptions
) — Cache the generated webpack modules and chunks to improve build speed. morebundler
('webpack' | 'rspack'
) - Option to choose a bundler.javaScriptLoader
('babel' | 'swc'
) - Option to choose a JavaScript loader.babel
((config: babel.TransformOptions, options: {configType: 'development' | 'production'; isSsr: boolean}) => babel.TransformOptions | Promise<babel.TransformOptions>
) - Allow override the default babel transform options.babelCacheDirectory
(boolean | string
) — Set directory for babel-loader cache (`default: node_modules/.cache/babel-loader``)swc
((config: SwcConfig, options: {configType: 'development' | 'production'; isSsr: boolean}) => SwcConfig | Promise<SwcConfig>
) - Allow override the default swc configuration.webpack
((config: webpack.Configuration, options: {configType: 'development' | 'production'; isSsr: boolean}) => webpack.Configuration | Promise<webpack.Configuration>
) - Allow override the default webpack configuration.rspack
((config: rspack.Configuration, options: {configType: 'development' | 'production'; isSsr: boolean}) => rspack.Configuration | Promise<rspack.Configuration>
) - Allow override the default rspack configuration.ssr
- build SSR bundle. The SSR entries should be insidesrc/ui/ssr
directory and match the client entries.noExternal
(string | RegExp | (string | RegExp)[] | true
) - prevent listed dependencies from being externalized for SSR. By default, all dependencies are externalized.moduleType
: ('commonjs' | 'esm'
) - library type for the SSR bundle, by defaultcommonjs
.
devServer
(Object
) — webpack dev server options.ipc
(string
) — the Unix socket to listen to. Ifipc
andport
are not defined, then the socket{rootDir}/dist/run/client.sock
is used.port
(number | true
) — specify a port number to listen for requests on. Iftrue
, the free port will be selected automatically.webSocketPath
(string
) — tells clients connected to devServer to use the provided path to connect. Default is${publicPathPrefix}/build/sockjs-node
.type
('https'
) — allow to serve over HTTPS.options
(import('https').ServerOptions
) — allow to provide your own certificate.
watchOptions
— a set of options used to customize watch mode, morewatchPackages
(boolean
) - watch all changes innode_modules
.
reactRefresh
(false | (options: ReactRefreshPluginOptions) => ReactRefreshPluginOptions
) — disable or configurereact-refresh
in dev mode, moredetectCircularDependencies
(true | CircularDependenciesOptions
) - detect modules with circular dependencies, morelazyCompilation
(true | LazyCompilationConfig
) — enable experimental lazy compilation featuretrue
— enable featureLazyCompilationConfig
port
(number
) — port where to listen to from the serverentries
(boolean=true
) — iffalse
- disables lazy compilation forsrc/ui/entries
folder content
analyzeBundle
(true | statoscope
) — tools to analyze bundle.true
— enable webpack-bundle-analyzer plugin. Report generated todist/public/build/stats.html
statoscope
— enable statoscope plugin. Reports generated todist/public/build/stats.json
anddist/public/build/report.json
reactProfiling
(boolean
) — use react profiler API in production, this option also disable minimization. The API is required by React developers tools for profile.statoscopeConfig
(Options
) —@statoscope/webpack-plugin
configuration options. Might be used to override the defaults. RequiresanalyzeBundle: statoscope
.cdn
(CdnUploadConfig | CdnUploadConfig[]
) - upload bundled client files to CDN.bucket
(string
) — bucket nameprefix
(string
) — path to files inside the bucketregion
(string
) — AWS region or any stringendpoint
(string
) - cdn host to upload filespublicPath
(string
) - public path to access files from the browsercompress
(boolean
) - upload also gzip and brotli compressed versions of filesadditionalPattern
(string[]
) — patterns for uploading additional files. By default, only files generated by webpack are loaded.
sentryConfig
(Options
) —@sentry/webpack-plugin
configuration options.
vendors
(string[] | (defaultVendors: string[]) => string[]
) — additional libraries or a function returning libraries for a vendor chunk;momentTz
— settings for moment-timezone (by default data is truncated);contextReplacement
(object
)highlight.js
(string[]
) — list of language names to include, e.g.['javascript', 'python', 'bash']
;locale
: (string[]=['ru']
) — list ofmoment.js
orday.js
locales to include, e.g.['de', 'es']
. LocaleEn
is always present.
safari10
(boolean
) — Enablessafari10
terser's option. Terser optionstransformCssWithLightningCss
(boolean
) — use Lighting CSS to transform and minimize css instead of PostCSS and cssnanolightningCssMinimizerOptions
((options: LightningCssMinimizerRspackPluginOptions) => LightningCssMinimizerRspackPluginOptions
) - modify or return a custom LightningCssMinimizerRspackPluginterser
((options: TerserOptions) => TerserOptions
) - modify or return a custom Terser options.
-
monaco
(object
) — use monaco-editor-webpack-pluginfileName
(string
) — custom filename template for worker scripts.languages
(string[]
) - include only a subset of the languages supported. If you don't need support for all languages, set needed languages explicitly, since it may significantly affect build time.features
(string[]
) - include only a subset of the editor features.customLanguages
(IFeatureDefinition[]
) - include custom languages (outside of the ones shipped with themonaco-editor
).
Web workers allow you to run JavaScript code in a separate thread from the main UI thread. This can improve the performance and responsiveness of your web application by offloading intensive tasks to the background.
To create a web worker, you need to write a script file that defines the logic of the worker. For example, this file (my.worker.ts) implements a simple function that adds two numbers and sends the result back to the main thread:
// my.worker.ts
self.onmessage = async (ev) => {
const {a = 0, b = 0} = ev.data || {};
const result = a + b;
self.postMessage({
result,
});
};
app-builder
provides built-in support for web workers for files with the .worker.[jt]s
suffix. You can choose
between two variants of getting web workers by setting the newWebWorkerSyntax
option:
newWebWorkerSyntax: false
(default) - use theworker-loader
to import web workers. Content of worker file will be included in main bundle as blob. This variant does not support dynamic imports inside worker. For example:
// main.ts
import MyWorker from './my.worker.ts';
const worker = new MyWorker();
In this variant, you need to add some type declarations for the worker files::
// worker.d.ts
declare module '*.worker.ts' {
class WebpackWorker extends Worker {}
export default WebpackWorker;
}
newWebWorkerSyntax: true
- use the webpack 5 web workers syntax to import web workers. This variant allows to use dynamic imports inside worker and load worker bundle from CDN. For example:
import {Worker} from '@gravity-ui/app-builder/worker';
const MyWorker = new Worker(new URL('./my.worker', import.meta.url));
To use the web worker in your main script, you need to communicate with it using the postMessage and onmessage methods. For example:
// main.ts
const worker = '...'; // Worker creation, first or second variant
worker.onmessage = ({data: {result}}) => {
console.log(result);
};
worker.postMessage({a: 1, b: 2});
Module Federation is a Webpack 5 feature that enables micro-frontend architecture, where JavaScript applications can dynamically load code from each other at runtime.
app-builder
uses @module-federation/enhanced
for advanced Module Federation support.
-
moduleFederation
(object
) — Module Federation configuration-
name
(string
) — unique name of the application in the Module Federation ecosystem. Required parameter. -
version
(string
) — application version. When specified, the entry file will be namedentry-{version}.js
instead ofentry.js
. -
disableManifest
(boolean
) — disable manifest file generation. Whentrue
, uses regular.js
files for remote entry instead of manifest files. Default isfalse
. -
remotes
(string[]
) — list of remote application names that this application can load. This is a simplified alternative tooriginalRemotes
that automatically generates remote URLs based on your public path configuration.How it works:
- In development mode: Remote URLs are automatically generated using the pattern
{commonPublicPath}{remoteName}/entry.js
(or manifest files if enabled) - In production mode: You need to ensure remote applications are deployed and accessible at the expected URLs
- Remote entry files are loaded at runtime to provide federated modules from other micro-frontends
File naming patterns (depends on configuration):
- With
disableManifest: false
(default):mf-manifest.json
ormf-manifest-[version].json
(with versioning) - With
disableManifest: true
:entry.js
orentry-[version].js
(with versioning)
Example:
// Simple configuration - URLs auto-generated in development remotes: ['header', 'navigation', 'footer']; // Results in loading from (in development): // - https://localhost:3000/header/mf-manifest.json // - https://localhost:3000/navigation/mf-manifest.json // - https://localhost:3000/footer/mf-manifest.json
Development vs Production:
- Development: App-builder automatically starts all remote applications and generates their URLs
- Production: Remote applications must be independently deployed and accessible at the generated URLs
Integration with other options:
- Works with
enabledRemotes
to selectively enable remotes in development - Affected by
remotesRuntimeVersioning
for versioned file names - File format controlled by
disableManifest
option
For more complex scenarios requiring custom URLs or cross-environment configurations, use
originalRemotes
instead. - In development mode: Remote URLs are automatically generated using the pattern
-
enabledRemotes
(string[]
) — list of enabled remotes for module federation. Development mode only. If not specified, all remotes from theremotes
array will be enabled by default.Purpose:
- Allows selective enabling/disabling of specific remotes during development
- Useful for debugging, testing individual micro-frontends, or working with partial system setups
- Helps reduce development startup time by loading only needed remotes
- Can be overridden via CLI flag:
--mf-remotes header footer
Loading behavior:
When CDN is disabled:
- Enabled remotes: Loaded from local development server with version-specific paths
- Disabled remotes: Loaded from
cdnPublicPath
(if configured), otherwise fallback to local paths
When CDN is enabled:
- All remotes: Use common public path,
enabledRemotes
selection has no effect on URLs
In production builds:
- This option is completely ignored, all configured remotes are included in the bundle
Example:
// Load all available remotes (header, navigation, footer) remotes: ['header', 'navigation', 'footer']; // Enable only specific remotes in development enabledRemotes: ['header', 'footer']; // navigation will be skipped
CLI Override:
# Override enabledRemotes from command line npx app-builder dev --mf-remotes header navigation
Note: This option has no effect in production builds - all configured remotes will be included in the production bundle configuration.
-
originalRemotes
(RemotesObject
) — full configuration of remote applications in Module Federation Plugin format. Use this when you need explicit control over remote URLs or for cross-environment deployments.When to use:
- Custom remote URLs (different domains, ports, paths)
- Cross-environment loading (staging, production CDN URLs)
- Complex deployment scenarios
- When
remotes
auto-generation doesn't meet your needs
Format:
originalRemotes: { remoteName: 'remoteName@remoteUrl', }
Examples:
originalRemotes: { header: 'header@https://cdn.example.com/header/entry.js', footer: 'footer@https://cdn.example.com/footer/entry.js', }
Note: When
originalRemotes
is specified, theremotes
option is ignored. Use eitherremotes
ORoriginalRemotes
, not both. -
remotesRuntimeVersioning
(boolean
) — enables runtime versioning for remote applications. When enabled, remote entry files include version information in their filenames, allowing for cache busting and version-specific loading.How it affects file names:
- With
disableManifest: false
:mf-manifest-[version].json
instead ofmf-manifest.json
- With
disableManifest: true
:entry-[version].js
instead ofentry.js
- Version is taken from the remote application's
version
configuration
Benefits:
- Cache busting: Different versions get different URLs, preventing browser caching issues
- Rollback capability: Can load specific versions of remotes
- Deployment safety: Gradual rollouts with version-specific remote loading
Important considerations:
- Without CDN: Version is inlined immediately during build time, as single build cannot contain multiple application versions
- With CDN: True runtime versioning is possible, versions are resolved dynamically at runtime
- Recommendation: Use
remotesRuntimeVersioning
together with CDN configuration for full dynamic versioning capabilities
Example with versioning enabled:
// Host application { moduleFederation: { name: 'shell', remotes: ['header', 'navigation'], remotesRuntimeVersioning: true, // Enable versioning } } // Remote application (header) { moduleFederation: { name: 'header', version: '2.1.0', // This version appears in filename exposes: { './Header': './src/Header' } } } // Results in loading: header/mf-manifest-2.1.0.json
Runtime behavior:
- The version is resolved at runtime from the remote's manifest or entry file
- Enables loading different versions of the same remote in different environments
- Works with both
remotes
andoriginalRemotes
configurations
- With
-
isolateStyles
(object
) — CSS style isolation settings to prevent conflicts between micro-frontends.getPrefix
((entryName: string) => string
) — function to generate CSS class prefix.prefixSelector
((prefix: string, selector: string, prefixedSelector: string, filePath: string) => string
) — function to add prefix to CSS selectors.
-
Also supports all standard options from @module-federation/enhanced, except
name
andremotes
, such as:filename
— entry file name (defaultentry.js
)exposes
— modules that this application exportsshared
— shared dependencies between applicationsruntimePlugins
— plugins for Module Federation runtime
-
Host Application Configuration Example:
Host applications consume remote modules from other micro-frontends:
export default defineConfig({
client: {
moduleFederation: {
name: 'shell',
// Simple remotes configuration
remotes: ['header', 'footer', 'sidebar'],
shared: {
react: {singleton: true, requiredVersion: '^18.0.0'},
'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
lodash: {singleton: true},
},
},
},
});
Advanced Host Configuration:
export default defineConfig({
client: {
moduleFederation: {
name: 'main-shell',
version: '2.1.0',
// Detailed remotes configuration
originalRemotes: {
header: 'header@https://cdn.example.com/header/entry.js',
footer: 'footer@https://cdn.example.com/footer/entry.js',
userProfile: 'userProfile@https://cdn.example.com/user-profile/entry.js',
},
remotesRuntimeVersioning: true,
isolateStyles: {
getPrefix: (entryName) => `.app-${entryName}`,
prefixSelector: (prefix, selector, prefixedSelector, filePath) => {
if (
[prefix, ':root', 'html', 'body', '.g-root', '.remote-app'].some((item) =>
selector.startsWith(item),
) ||
filePath.includes('@gravity-ui/chartkit')
) {
return selector;
}
return prefixedSelector;
},
},
shared: {
react: {singleton: true, requiredVersion: '^18.0.0'},
'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
lodash: {singleton: true},
},
},
},
});
Remote Application Configuration Example:
Remote applications expose their modules for consumption by host applications:
export default defineConfig({
client: {
moduleFederation: {
name: 'header',
// Expose modules for other applications
exposes: {
'./Header': './src/components/Header',
'./Navigation': './src/components/Navigation',
'./UserMenu': './src/components/UserMenu',
},
shared: {
react: {singleton: true, requiredVersion: '^18.0.0'},
'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
lodash: {singleton: true},
},
},
},
});
Bidirectional Configuration Example:
Applications can be both host and remote simultaneously:
export default defineConfig({
client: {
moduleFederation: {
name: 'dashboard',
version: '1.5.0',
// Consume remote modules
remotes: ['charts', 'notifications'],
// Expose own modules
exposes: {
'./DashboardLayout': './src/layouts/DashboardLayout',
'./DataTable': './src/components/DataTable',
},
shared: {
react: {singleton: true, requiredVersion: '^18.0.0'},
'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
lodash: {singleton: true},
},
},
},
});
Advanced Remotes Configuration Examples:
export default defineConfig({
client: {
moduleFederation: {
name: 'advanced-shell',
version: '3.0.0',
// Example 1: Simple remotes for development
remotes: ['header', 'sidebar', 'footer', 'analytics'],
// Example 2: Enable only specific remotes in development
enabledRemotes: ['header', 'sidebar'], // Only these will load in dev mode
// Example 3: Runtime versioning with manifests (production-ready)
remotesRuntimeVersioning: true, // Enables version-specific loading
disableManifest: false, // Use mf-manifest-[version].json files
shared: {
react: {singleton: true, requiredVersion: '^18.0.0'},
'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
},
},
},
});
// Alternative: Explicit URLs for production
export default defineConfig({
client: {
moduleFederation: {
name: 'production-shell',
// Use originalRemotes for explicit control
originalRemotes: {
// Static CDN URLs
header: 'header@https://cdn.company.com/header/mf-manifest-2.1.0.json',
sidebar: 'sidebar@https://cdn.company.com/sidebar/entry-1.5.0.js',
// Dynamic remote loading
analytics: `promise new Promise((resolve) => {
const remoteUrl = process.env.NODE_ENV === 'production'
? 'https://analytics.cdn.com/entry.js'
: 'http://localhost:3003/entry.js';
resolve(\`analytics@\${remoteUrl}\`);
})`,
},
shared: {
react: {singleton: true, requiredVersion: '^18.0.0'},
'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
},
},
},
});
Development Workflow with Remotes:
# Start host application with all remotes
npx app-builder dev
# Start host with only specific remotes (faster development)
npx app-builder dev --mf-remotes header sidebar
# The above is equivalent to setting enabledRemotes in config:
# enabledRemotes: ['header', 'sidebar']