Building Multi Framework Dashboard With Astro

Combine React, Vue, and Svelte components in a single high-performance dashboard using Astro's island architecture

Modern dashboards often require diverse UI components--some optimized for real-time data visualization, others for complex forms or interactive charts. Astro's unique architecture allows you to use multiple frontend frameworks within a single project, combining React's vast ecosystem, Vue's gentle learning curve, and Svelte's exceptional performance where each shines best. This guide walks through building a production-ready multi-framework dashboard that leverages the strengths of each technology while maintaining a cohesive user experience. Our web development services team specializes in building custom dashboards and applications using modern frameworks.

By following this approach, you can build dashboards that load faster than traditional single-framework solutions while giving your team the flexibility to work with familiar technologies. The island architecture that Astro pioneered makes this possible by treating each framework component as an isolated island that hydrates independently.

Why Use Multiple Frameworks

Each framework brings unique strengths to specific dashboard components

React for Complex Visualizations

Leverage extensive charting libraries like Recharts and Victory for data-heavy widgets

Vue for Interactive Forms

Build filter panels and configuration forms rapidly with Vue's intuitive template syntax

Svelte for Real-Time Displays

Create live feeds and streaming data components with Svelte's compiled performance

Astro for Layout & Structure

Build static dashboard shells with zero JavaScript for optimal initial load

The Island Architecture Advantage

Astro's island architecture fundamentally changes how we think about dashboard performance. Traditional single-page applications load the entire framework JavaScript upfront, even for static content. With Astro's islands, components render to static HTML by default, and JavaScript is only delivered for interactive elements marked with client directives. This approach aligns with performance optimization strategies that prioritize fast load times and efficient resource usage.

Consider a dashboard with ten widgets: a real-time metrics chart, a data table with sorting, a form for filtering, several static summary cards, and a navigation sidebar. In a traditional React app, all framework code loads before anything becomes interactive. With Astro's islands, the static summary cards ship no JavaScript at all. The navigation sidebar loads minimal hydration code. The data table hydrates only when it becomes visible. Each widget controls its own JavaScript payload independently.

This granular approach means your dashboard's Time to Interactive improves dramatically, especially on mobile devices or slower connections. The browser processes HTML immediately while framework code downloads in parallel. Users see meaningful content before the JavaScript finishes loading, creating a perception of faster performance regardless of actual download speeds.

Setting Up Your Multi-Framework Project

Initialize an Astro project with the integrations you need. For a dashboard, you'll typically want React for its charting ecosystem, Vue for form components, and Svelte for real-time displays.

# Create new Astro project
npm create astro@latest my-dashboard

# Add React integration
npx astro add react

# Add Vue integration
npx astro add vue

# Add Svelte integration
npx astro add svelte

Each integration command modifies your astro.config.mjs file automatically, adding the necessary build configuration and compiler options. The process handles setting up Vite, the underlying bundler, to process framework-specific syntax and transformations. After installation, your configuration includes the framework packages as dependencies and configures Astro to render components from each framework during build time.

Verify your configuration by checking astro.config.mjs, which should contain an integrations array listing each framework. You can also configure framework-specific options here, such as React's JSX transform or Vue's template compiler customizations.

Project Structure for Dashboards

Organize a multi-framework dashboard with clear boundaries between framework-specific code. Keep components organized by framework in dedicated directories, making it obvious which technology each file uses. This structure scales well as your dashboard grows and new team members can immediately understand where to find components of each type.

src/
├── components/
│ ├── react/ # React components (charts, data tables)
│ ├── vue/ # Vue components (forms, filters)
│ ├── svelte/ # Svelte components (real-time feeds)
│ └── astro/ # Astro components (layout, static UI)
├── layouts/
│ └── DashboardLayout.astro
├── pages/
│ └── index.astro
└── styles/
 └── global.css

The Astro directory holds layout and wrapper components that provide structure without framework dependencies. Framework-specific directories contain only components that genuinely require that technology, preventing over-engineering where simple Astro components would suffice. For related patterns in organizing complex web applications, see our guide on building web components with Svelte.

React Components: Charts and Data Tables

React's strength lies in its mature ecosystem of visualization libraries. Components for charts, data tables, and complex interactive displays benefit most from React's extensive library support. The example below shows a metrics chart component using Recharts, a popular charting library.

import React, { useState, useMemo } from 'react';
import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts';

export default function MetricsChart({ data, title }) {
 const [timeRange, setTimeRange] = useState('7d');

 const filteredData = useMemo(() => {
 const days = timeRange === '7d' ? 7 : timeRange === '30d' ? 30 : 90;
 return data.slice(-days);
 }, [data, timeRange]);

 return (
 <div className="chart-container p-4 bg-white rounded-lg shadow">
 <div className="flex justify-between items-center mb-4">
 <h3 className="text-lg font-semibold">{title}</h3>
 <select
 value={timeRange}
 onChange={(e) => setTimeRange(e.target.value)}
 className="border rounded px-2 py-1"
 >
 <option value="7d">Last 7 days</option>
 <option value="30d">Last 30 days</option>
 <option value="90d">Last 90 days</option>
 </select>
 </div>
 <ResponsiveContainer width="100%" height={300}>
 <LineChart data={filteredData}>
 <XAxis dataKey="date" />
 <YAxis />
 <Tooltip />
 <Line type="monotone" dataKey="value" stroke="#3b82f6" strokeWidth={2} />
 </LineChart>
 </ResponsiveContainer>
 </div>
 );
}

This component demonstrates React's strengths: local state management with hooks, memoization for performance optimization, and integration with a rich library ecosystem. The component handles its own state for the time range selector, filtering data efficiently using useMemo to avoid unnecessary recalculations. Recharts handles all the complex SVG rendering logic, allowing you to focus on dashboard-specific behavior.

Vue Components: Interactive Forms

Vue's intuitive reactivity system excels at form handling. Dashboard filters, configuration panels, and settings forms benefit from Vue's two-way binding and clear template syntax. The example below shows a filter form component with reactive state.

<script setup>
import { ref, computed } from 'vue';

const props = defineProps({
 filters: { type: Object, required: true }
});

const emit = defineEmits(['update:filters']);
const localFilters = ref({ ...props.filters });

function applyFilters() {
 emit('update:filters', { ...localFilters.value });
}

function resetFilters() {
 localFilters.value = { status: 'all', priority: 'all', dateRange: 'week' };
 applyFilters();
}
</script>

<template>
 <div class="filter-panel p-4 bg-white rounded-lg shadow">
 <h3 class="text-lg font-semibold mb-4">Filters</h3>
 <div class="filter-group mb-4">
 <label class="block text-sm font-medium mb-1">Status</label>
 <select v-model="localFilters.status" class="w-full border rounded px-3 py-2">
 <option value="all">All Statuses</option>
 <option value="active">Active</option>
 <option value="pending">Pending</option>
 <option value="completed">Completed</option>
 </select>
 </div>
 <div class="button-group flex gap-2">
 <button @click="applyFilters" class="bg-blue-600 text-white px-4 py-2 rounded">
 Apply
 </button>
 <button @click="resetFilters" class="bg-gray-200 px-4 py-2 rounded">
 Reset
 </button>
 </div>
 </div>
</template>

Vue's template syntax makes form handling declarative and readable. The v-model directive creates two-way binding between form inputs and component state, eliminating boilerplate update logic. The component emits events to communicate changes to parent components, following Vue's established patterns for component communication.

Svelte Components: Real-Time Data Displays

Svelte's compiled approach produces highly efficient code for components that update frequently. Real-time metrics, live notifications, and streaming data displays benefit from Svelte's direct DOM manipulation without virtual DOM overhead. The example below shows a real-time activity feed.

<script>
 export let activities = [];
 let isConnected = false;
 let connectionStatus = 'disconnected';

 function connectToFeed() {
 isConnected = true;
 connectionStatus = 'connecting';
 
 const interval = setInterval(() => {
 const newActivity = {
 id: Date.now(),
 type: getRandomActivityType(),
 message: getRandomMessage(),
 timestamp: new Date().toLocaleTimeString()
 };
 activities = [newActivity, ...activities].slice(0, 50);
 }, 2500);
 
 setTimeout(() => { connectionStatus = 'connected'; }, 1000);
 return () => clearInterval(interval);
 }

 function getRandomActivityType() {
 return ['sale', 'signup', 'alert', 'update'][Math.floor(Math.random() * 4)];
 }

 function getRandomMessage() {
 const messages = [
 'New customer registration',
 'Completed purchase - $149',
 'Server alert: high memory usage',
 'Dashboard configuration updated'
 ];
 return messages[Math.floor(Math.random() * messages.length)];
 }
</script>

<div class="activity-feed p-4 bg-white rounded-lg shadow">
 <div class="header flex justify-between items-center mb-4">
 <h3 class="text-lg font-semibold">Live Activity</h3>
 <span class="status-dot w-3 h-3 rounded-full" class:bg-green-500={connectionStatus === 'connected'}></span>
 </div>
 
 {#if !isConnected}
 <button on:click={connectToFeed} class="bg-green-600 text-white px-4 py-2 rounded">
 Connect to Live Feed
 </button>
 {:else}
 <ul class="feed-list space-y-2 max-h-96 overflow-y-auto">
 {#each activities as activity (activity.id)}
 <li class="activity-item p-3 border rounded hover:bg-gray-50">
 <span class="text-xs font-semibold text-gray-500 uppercase">{{ activity.type }}</span>
 <p class="mt-1">{{ activity.message }}</p>
 </li>
 {/each}
 </ul>
 {/if}
</div>

Svelte's reactivity model handles continuous updates elegantly. The component assigns directly to the activities array, and Svelte's compiler generates optimal DOM update code. The #each block with keyed identity ensures efficient list updates when new activities arrive. For more on Svelte's capabilities, explore our guide on building web components with Svelte.

Integrating Components into Dashboard Layouts

Astro components serve as the glue that brings framework-specific widgets together into a cohesive dashboard. These static components define layout, handle navigation, and provide structural elements that don't require JavaScript to function.

---
// src/layouts/DashboardLayout.astro
import Sidebar from '../components/astro/Sidebar.astro';
import Header from '../components/astro/Header.astro';
const { title = 'Dashboard' } = Astro.props;
---

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>{title} | My Dashboard</title>
</head>
<body class="bg-gray-100 min-h-screen">
 <div class="flex h-screen overflow-hidden">
 <Sidebar />
 <div class="flex-1 flex flex-col overflow-hidden">
 <Header title={title} />
 <main class="flex-1 overflow-auto p-6">
 <slot />
 </main>
 </div>
 </div>
</body>
</html>

The layout component establishes the dashboard structure using Astro's component composition. The <slot /> element defines where child content renders, allowing individual pages to inject their specific widgets. This static layout ships zero JavaScript to the browser, improving initial load performance. All interactive elements within the layout use client directives on their framework-specific child components.

Composing the Dashboard Page

---
import DashboardLayout from '../layouts/DashboardLayout.astro';
import MetricsChart from '../components/react/MetricsChart';
import FilterPanel from '../components/vue/FilterPanel.vue';
import ActivityFeed from '../components/svelte/ActivityFeed.svelte';
import SummaryCards from '../components/astro/SummaryCards.astro';

const chartData = [
 { date: '2024-01-01', value: 120 },
 { date: '2024-01-02', value: 150 },
 { date: '2024-01-03', value: 180 },
 { date: '2024-01-04', value: 140 },
 { date: '2024-01-05', value: 200 },
 { date: '2024-01-06', value: 175 },
 { date: '2024-01-07', value: 220 },
];
---

<DashboardLayout title="Analytics Dashboard">
 <div class="dashboard-grid grid gap-6">
 <!-- Summary Cards: Static, zero JS -->
 <SummaryCards />

 <!-- Filter Panel: Vue for reactive forms -->
 <FilterPanel client:visible />

 <!-- Metrics Chart: React for charting -->
 <MetricsChart data={chartData} title="Weekly Metrics" client:visible />

 <!-- Activity Feed: Svelte for real-time -->
 <ActivityFeed client:visible />
 </div>
</DashboardLayout>

This page demonstrates how client directives control hydration behavior. Components with client:visible only load their JavaScript when they enter the viewport, reducing initial bundle size. Static Astro components like SummaryCards render as pure HTML without any JavaScript. This selective hydration strategy gives you granular control over when each framework's code executes.

Managing Shared State Across Frameworks

Multi-framework dashboards often need to share state--filter selections affect multiple widgets, authentication state persists across components, and real-time data updates propagate throughout the interface. Nano stores provide a lightweight state management solution that works across all frameworks.

Unlike framework-specific solutions like Redux or Vue's Pinia, Nano stores use plain JavaScript observables that any framework can subscribe to. This makes it ideal for sharing state between React, Vue, and Svelte components.

// src/stores/dashboardStore.js
import { atom, map } from 'nanostores';

export const $filters = atom({
 status: 'all',
 priority: 'all',
 dateRange: 'week'
});

export const $user = map({ id: null, name: '', permissions: [] });

export function setFilters(newFilters) {
 $filters.set({ ...$filters.get(), ...newFilters });
}

Nano stores' framework adapters provide idiomatic syntax for each ecosystem while underlying state remains unified. A filter change in a Vue component immediately reflects in React and Svelte components subscribed to the same store. For simpler cross-component communication, native browser events also work well across framework boundaries.

Performance Optimization Strategies

Multi-framework dashboards require careful attention to performance. While Astro's island architecture provides a strong foundation, how you implement individual components and structure your project significantly impacts load times and runtime performance.

Lazy Loading with Client Directives

Components with client:visible only load their JavaScript when they enter the viewport, reducing initial bundle size:

<DataTable client:visible={{ rootMargin: "200px" }} data={largeDataset} />

The rootMargin option allows preloading before the component becomes visible, creating a smoother experience for users who scroll quickly. For dashboards with many widgets, consider implementing a component registry that loads widgets dynamically based on user navigation or viewport position.

Minimizing Framework Footprint

Each framework integration adds to your build output, so use only the frameworks you need. If your dashboard uses React for charts and Vue for forms but Svelte components aren't necessary, remove the Svelte integration to reduce bundle size. For React components, prefer functional components with hooks over class components to enable better tree-shaking. Vue's Composition API produces smaller compiled output than the Options API in many cases.

Deployment Considerations

Deploying multi-framework Astro dashboards follows similar patterns to single-framework projects. Static exports work for dashboards without server-side rendering--generate HTML files during build and serve them from a CDN. Many hosting platforms support this pattern natively, including Vercel, Netlify, and Cloudflare Pages.

For dashboards requiring server-side features like authentication or database connections, Astro's hybrid rendering mode allows static pages with dynamic API routes. Your dashboard pages remain static while backend functionality lives in API endpoints that run serverlessly. This hybrid approach balances performance with functionality requirements.

Conclusion

Building a multi-framework dashboard with Astro combines the best of each framework's ecosystem while maintaining excellent performance through island architecture. React's charting libraries, Vue's form handling ergonomics, and Svelte's real-time performance all contribute to a dashboard that leverages each technology's strengths. The key lies in thoughtful architecture--using frameworks where they excel while letting Astro handle layout and structure with zero-JS components. Our experienced web development team can help you architect and build custom dashboards tailored to your business needs.

Start by setting up Astro with your required integrations, then incrementally add framework-specific components as needed. Use Nano stores or events for cross-framework communication, and apply client directives strategically to optimize loading performance. The result is a maintainable dashboard that performs well, scales effectively, and allows your team to work with familiar technologies throughout.

Ready to Build Your Custom Dashboard?

Our team specializes in creating high-performance web applications using modern frameworks like Astro, React, Vue, and Svelte. Contact us to discuss your dashboard project.

Frequently Asked Questions

Sources

  1. LogRocket Blog - Building a multi-framework dashboard with Astro - Technical implementation details and code examples for Astro multi-framework dashboards
  2. GitHub - EmaSuriano/astro-multi-framework-dashboard - Open source implementation demonstrating multi-framework architecture
  3. Astro Documentation - Integrations Reference - Official Astro integration API documentation