Vite plugin for dynamic external remotes with window variable resolution
npm install @kpsoftec/vite-external-remotes-pluginA Vite plugin for dynamic external remotes with window variable resolution in microfrontend applications. Enables runtime configuration of remote URLs for Module Federation.
- ✅ Dynamic remote URL resolution using window variables
- ✅ Support for multiple pattern formats
- ✅ Runtime window variable replacement
- ✅ Plugin options (debug, variablePrefix, patterns)
- ✅ TypeScript support
- ✅ Debug logging
``bash`
npm install @kpsoftec/vite-external-remotes-plugin
`javascript
// vite.config.js
import { defineConfig } from 'vite'
import ExternalRemotesPlugin from '@kpsoftec/vite-external-remotes-plugin'
import federation from '@originjs/vite-plugin-federation'
export default defineConfig({
plugins: [
ExternalRemotesPlugin(),
federation({
name: 'host-app',
remotes: {
remote_app: '@[window.remote_app_url]/assets/remoteEntry.js',
remote_two: 'remote@[window.remote_two_url]/remoteEntry.js'
},
shared: ['react', 'react-dom']
})
]
})
`
| Pattern | Description | Example |
|---------|-------------|---------|
| @[window.variable]/path | Direct window variable URL | @[window.remote_url]/remoteEntry.js |remote@[window.variable]/path
| | Federation remote with window variable | myRemote@[window.base_url]/remoteEntry.js |[window.variable]
| | Standalone window variable | [window.full_remote_url] |@[window.variable]
| | Standalone @ syntax | @[window.remote_entry_url] |
`javascript`
federation({
name: 'host-app',
remotes: {
// Simple pattern
remote_app: '@[window.remote_app_url]/remoteEntry.js',
// With remote name
user_service: 'userService@[window.user_service_url]/remoteEntry.js',
// Multiple variables in path
api_service: '@[window.api_base_url]/services/[window.service_name]/remoteEntry.js'
}
})
`javascript`
remotes: {
// Cache busting with build hash
remote_app: '@[window.remote_url]/remoteEntry.js?v=[window.BUILD_HASH]',
// Multiple parameters
remote_two: '@[window.api_url]/remoteEntry.js?env=[window.ENV]&v=[window.VERSION]'
}
`javascript
// main.jsx
async function loadRemoteConfig() {
try {
const response = await fetch('/config/remotes.json')
const config = await response.json()
// Set window variables
Object.assign(window, config)
// Import app after config is loaded
await import('./bootstrap')
} catch (error) {
console.error('Failed to load remote config:', error)
}
}
loadRemoteConfig()
`
`json`
// public/config/remotes.json
{
"remote_app_url": "http://localhost:4173",
"user_service_url": "https://user-service.example.com",
"BUILD_HASH": "abc123def456"
}
`javascript
// config/remotes.js
const configs = {
development: {
remote_app_url: 'http://localhost:4001',
user_service_url: 'http://localhost:4002',
BUILD_HASH: 'dev'
},
production: {
remote_app_url: 'https://remote1.example.com',
user_service_url: 'https://user-service.example.com',
BUILD_HASH: process.env.VITE_BUILD_HASH
}
}
const env = import.meta.env.MODE || 'development'
Object.assign(window, configs[env])
`
`javascript
// Set before app initialization
window.remote_app_url = 'https://my-remote.example.com'
window.BUILD_HASH = '12345'
// Then import your app
import('./App')
`
`javascript`
// vite.config.js
ExternalRemotesPlugin({
// Custom variable prefix (default: 'window.')
variablePrefix: 'globalThis.',
// Enable debug logging
debug: true,
// Custom pattern matching
patterns: [
/\[window\.[\w_]+\]/g,
/\[globalThis\.[\w_]+\]/g
]
})
`typescript
// types/window.d.ts
declare global {
interface Window {
remote_app_url: string;
user_service_url: string;
BUILD_HASH: string;
ENV: 'development' | 'staging' | 'production';
}
}
export {};
`
`typescript
// config.ts
import type { ExternalRemotesPluginOptions } from '@kpsoftec/vite-external-remotes-plugin';
interface RemoteConfig {
remote_app_url: string;
user_service_url: string;
BUILD_HASH: string;
}
export async function loadTypedConfig(): Promise
const response = await fetch('/config/remotes.json');
const config: RemoteConfig = await response.json();
// Type-safe assignment
Object.assign(window, config);
return config;
}
`
Enable debug logging to see pattern replacements:
`javascript`
ExternalRemotesPlugin({ debug: true })
This will log:
- Which files are being processed
- What patterns are being replaced
- Available window variables
The plugin transforms federation configuration at build time by replacing window variable patterns with actual window property access:
Before transformation:
`javascript`
remotes: {
remote_app: '@[window.remote_url]/remoteEntry.js?v=[window.BUILD_HASH]'
}
After transformation:
`javascript`
remotes: {
remote_app: window.remote_url + "/remoteEntry.js?v=" + window.BUILD_HASH
}
1. Load configuration before app initialization
`javascript`
// ✅ Correct
await loadConfig()
await import('./App')
// ❌ Wrong - app loads before config
import('./App')
loadConfig()
2. Provide fallback values
`javascript`
window.remote_url = window.remote_url || 'https://fallback.example.com'
3. Validate required variables
`javascriptMissing config: ${missing.join(', ')}
const required = ['remote_app_url', 'BUILD_HASH']
const missing = required.filter(key => !window[key])
if (missing.length > 0) {
throw new Error()`
}
$3
- Verify remote URLs are accessible
- Check if BUILD_HASH or other variables are correctly set$3
- Ensure the plugin is loaded before the federation plugin
- Check that patterns match exactly: [window.variable_name]API Reference
$3
Parameters:
-
options.variablePrefix: string - Variable prefix (default: 'window.')
- options.debug: boolean - Enable debug logging (default: false)
- options.patterns: RegExp[] - Custom pattern matchingReturns: Vite plugin that transforms federation remote configurations
Supported Patterns:
-
@[window.variable]/path → window.variable + "/path"
- remote@[window.variable]/path → window.variable + "/path"
- [window.variable] → window.variable`MIT