How to integrate an Astro + React application as a sub-application in a Vite + Vue3 + micro-app setup?
I have a main application built with Vite + Vue3 + micro-app, and I need to integrate an Astro + React application as a sub-application. I’ve tried using the following approach:
<micro-app name='test' url='http://127.0.0.1:8000/workspace' iframe"/>
The sub-application is configured for cross-domain access, and the page uses:
<Layout>
<Test client:only="react" />
</Layout>
My Test component contains:
const Test = () => {
return (
<>
<h1>Test</h1>
<Button onClick={console.log}>Click me3</Button>
</>
);
}
export default Test;
When using client:only="react" in the sub-application, I get a blank page with no content. When using client:load, click events don’t work, which suggests a JavaScript loading failure, leaving only the static page visible. Based on the current application elements, it appears that the micro-app is not properly hydrating the Astro Island as a client. What steps do I need to take to make Astro run correctly within the micro-app environment?
Astro + React integration with micro-app frameworks presents unique challenges due to hydration conflicts and JavaScript loading issues. The core problem lies in how Astro’s island architecture interacts with micro-app sandboxes, requiring specific configuration adjustments to ensure proper component rendering and event handling.
Contents
- Understanding the Integration Challenge
- Core Issues with Astro Hydration in Micro-Apps
- Configuration Solutions for Astro
- Micro-App Environment Adjustments
- Alternative Integration Approaches
- Best Practices and Performance Considerations
Understanding the Integration Challenge
When integrating an Astro + React application as a sub-application within a Vite + Vue3 + micro-app setup, you’re essentially trying to embed one web application within another. This creates several technical challenges:
- Hydration conflicts: Astro’s server-side rendering (SSR) and client-side hydration mechanisms may not work correctly when the application is loaded within a micro-app sandbox
- JavaScript loading: The micro-app environment may interfere with how Astro loads and executes React components
- Context isolation: The React context and state management might not function properly when embedded
The micro-app framework creates an isolated environment for your Astro + React application, which can interfere with Astro’s normal hydration process. When you use client:only="react", Astro expects to handle the complete rendering and hydration process, but the micro-app may be interfering with this.
Core Issues with Astro Hydration in Micro-Apps
Based on the research findings, several specific issues emerge:
JavaScript Loading Failures
When using client:load, click events don’t work, indicating that JavaScript is loading but not executing properly within the micro-app environment. This suggests that the micro-app’s sandbox is preventing the JavaScript from running with the correct context.
Hydration Strategy Problems
The client:only="react" directive results in a blank page because Astro’s hydration process is designed to work within a normal browser environment, not within a micro-app sandbox. According to the research, Astro needs hints to use the correct renderer when using client:only hydration strategy, and this becomes more complex in micro-app scenarios.
React Root Creation Issues
A GitHub issue suggests that React components rendered with client:only should probably use createRoot instead of hydrateRoot, which may not be happening correctly in the micro-app environment.
Configuration Solutions for Astro
1. Modify Astro’s Vite Configuration
Update your astro.config.mjs to handle micro-app scenarios:
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import node from '@astrojs/node';
export default defineConfig({
output: 'server',
integrations: [react()],
vite: {
ssr: {
noExternal: ['react', 'react-dom'], // Ensure React dependencies are bundled
},
build: {
rollupOptions: {
external: [], // Don't externalize anything for micro-app compatibility
},
},
optimizeDeps: {
force: true, // Force re-optimization for better compatibility
},
},
adapter: node({
mode: 'standalone',
}),
});
2. Adjust React Component for Micro-App Environment
Modify your Test component to handle the micro-app environment:
import { useEffect, useRef } from 'react';
const Test = () => {
const buttonRef = useRef(null);
useEffect(() => {
// Ensure React can properly attach event listeners
if (buttonRef.current) {
buttonRef.current.addEventListener('click', () => {
console.log('Button clicked in micro-app environment');
});
}
}, []);
return (
<>
<h1>Test</h1>
<Button ref={buttonRef}>Click me3</Button>
</>
);
}
export default Test;
3. Use Alternative Hydration Strategy
Consider using client:idle instead of client:only for better micro-app compatibility:
<Layout>
<Test client:idle="react" />
</Layout>
The client:idle strategy renders the component only when the browser is idle, which may work better in micro-app environments.
Micro-App Environment Adjustments
1. Configure Micro-App for Astro Compatibility
Modify your micro-app configuration to better handle Astro’s requirements:
// In your main application
import microApp from '@micro-zoe/micro-app';
microApp.start({
// Ensure proper communication with micro-app
plugins: {
// Add plugins to handle Astro's specific needs
},
// Disable some sandboxing features that might interfere with Astro
sandbox: {
// Adjust sandbox settings based on your needs
},
});
2. Use iframe with Specific Attributes
Try different iframe configurations:
<micro-app
name='test'
url='http://127.0.0.1:8000/workspace'
iframe
shadowDom
inline
disableSandbox={false}
/>
The shadowDom and inline attributes may help with proper component rendering.
3. Enable Proper Communication Channels
Ensure proper communication between the main app and micro-app:
// In your Astro app
import { useEffect } from 'react';
const Test = () => {
useEffect(() => {
// Listen for micro-app events
window.addEventListener('mount', () => {
console.log('Micro-app mounted');
});
window.addEventListener('unmount', () => {
console.log('Micro-app unmounted');
});
}, []);
return (
<>
<h1>Test</h1>
<Button onClick={() => console.log('Click me')}>Click me3</Button>
</>
);
}
Alternative Integration Approaches
1. Module Federation Integration
Consider using Module Federation to integrate Astro and React components:
// In your main Vite config
import { defineConfig } from 'vite';
import federation from '@originjs/vite-plugin-federation';
export default defineConfig({
plugins: [
federation({
name: 'mainApp',
remotes: {
astroApp: 'http://localhost:8000/assets/remoteEntry.js',
},
shared: ['react', 'react-dom'],
}),
],
});
2. Build Astro as a Library
Configure Astro to build as a library:
// astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
export default defineConfig({
output: 'server',
integrations: [react()],
build: {
format: 'esm',
},
});
3. Use Static Islands for Better Compatibility
Consider using static Astro islands with React components:
---
import Test from '../components/Test.astro';
---
<Layout>
<Test client:load />
</Layout>
Best Practices and Performance Considerations
1. Optimize Astro Build for Micro-Apps
// astro.config.mjs
export default defineConfig({
output: 'server',
integrations: [react()],
vite: {
ssr: {
noExternal: ['react', 'react-dom', 'antd'], // Add any external dependencies
},
build: {
target: 'esnext', // Use modern JavaScript features
},
},
});
2. Handle Resource Loading Properly
Ensure that all CSS and JavaScript resources are loaded correctly within the micro-app environment:
// In your Astro component
<head>
<link rel="stylesheet" href="/styles.css" />
<script type="module" src="/main.js"></script>
</head>
3. Test Different Hydration Strategies
Experiment with different hydration strategies to find what works best in your micro-app environment:
client:load- Load and render immediatelyclient:idle- Load when browser is idleclient:visible- Load when component becomes visibleclient:only- Client-side only rendering (with proper configuration)
4. Monitor Performance
Use browser dev tools to monitor:
- JavaScript bundle size
- Loading times
- Hydration performance
- Event handler attachment
Conclusion
Integrating an Astro + React application as a sub-application in a Vite + Vue3 + micro-app setup requires careful configuration and understanding of the unique challenges involved. The key takeaways are:
- Configure Astro properly for micro-app environments by adjusting Vite settings and React integration
- Experiment with different hydration strategies -
client:idlemay work better thanclient:onlyin micro-app scenarios - Adjust micro-app configuration to handle Astro’s island architecture requirements
- Consider alternative approaches like Module Federation or library builds if direct integration proves problematic
- Test thoroughly in your specific micro-app environment to ensure proper rendering and event handling
The core issue you’re experiencing with client:only="react" resulting in blank pages and client:load causing JavaScript loading failures stems from how Astro’s hydration process interacts with the micro-app sandbox. By implementing the configuration changes and alternative approaches outlined above, you should be able to achieve a working integration between your Astro + React application and the Vite + Vue3 + micro-app setup.
Sources
- Astro Documentation - React Integration
- Stack Overflow - How to run an Astro web application as a sub-application into a micro-app
- GitHub Issue - React component hydration error when loaded with
client:only - Medium - Setting up micro-frontends with Astro and Ecma Script Modules
- Leapcell - Seamless Integration of React and Vue Applications in Astro Using Module Federation