Frameworks
Maravilla supports modern full-stack frameworks through dedicated adapters and a Nitro preset. Each integration produces a .maravilla/ build output that the runtime can serve at the edge.
SvelteKit
Fully supported via @maravilla-labs/adapter-sveltekit.
Install
npm install -D @maravilla-labs/adapter-sveltekit
npm install @maravilla-labs/platform
Configure
Update your svelte.config.js to use the Maravilla adapter:
import adapter from '@maravilla-labs/adapter-sveltekit';
const config = {
kit: {
adapter: adapter({
out: 'build', // Output directory (default: '.maravilla')
precompress: false, // Pre-compress static assets (default: false)
polyfill: true // Include polyfills (default: true)
})
}
};
export default config;
Adapter options
| Option | Type | Default | Description |
|---|---|---|---|
out | string | '.maravilla' | Output directory for the build |
precompress | boolean | false | Pre-compress static assets with gzip/brotli |
polyfill | boolean | true | Include runtime polyfills |
envPrefix | string | 'PUBLIC_' | Prefix for client-exposed environment variables |
include / exclude | string[] | ['/*'] / [] | Route include/exclude patterns |
external | string[] | [] | Dependencies to exclude from the server bundle |
Use platform services
Access KV and Database in your server-side load functions:
// src/routes/+page.server.ts
import { getPlatform } from '@maravilla-labs/platform';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async () => {
const platform = getPlatform();
// KV store
const cached = await platform.KV.demo.get('homepage:data');
// Document database (document queries)
const users = await platform.DB.find('users', { active: true });
return { cached, users };
};
React Router
Fully supported via @maravilla-labs/adapter-react-router. Works with React Router 7 (the framework formerly known as Remix).
Install
npm install -D @maravilla-labs/adapter-react-router
npm install @maravilla-labs/platform
Configure
Add the Maravilla Vite plugin to your vite.config.ts. The plugin runs after React Router’s build to produce the .maravilla/ bundle:
import { defineConfig } from 'vite';
import { reactRouter } from '@react-router/dev/vite';
import { maravillaReactRouter } from '@maravilla-labs/adapter-react-router/vite';
export default defineConfig({
plugins: [
reactRouter(),
maravillaReactRouter({
out: '.maravilla', // Output directory (default: '.maravilla')
serverBuildPath: 'build/server', // React Router server output (default)
clientBuildPath: 'build/client', // React Router client output (default)
})
]
});
Adapter options
| Option | Type | Default | Description |
|---|---|---|---|
out | string | '.maravilla' | Output directory |
serverBuildPath | string | 'build/server' | Path to React Router server build |
clientBuildPath | string | 'build/client' | Path to React Router client build |
serverBuildFile | string | 'index.js' | Server build entry file name |
envPrefix | string | 'PUBLIC_' | Prefix for client-exposed environment variables |
polyfill | boolean | true | Include runtime polyfills |
precompress | boolean | false | Pre-compress static assets |
external | string[] | [] | Dependencies to exclude from the bundle |
Astro (experimental)
Astro support is experimental. You can install it and deploy with it today, but the integration is young: the API may still change between releases, and we don’t recommend it for production workloads yet. Everything else on this page is stable. Tell us what breaks — early reports shape what stabilises first.
Supported via @maravilla-labs/adapter-astro, which works with Astro 6 and Astro 7. Server-rendered pages and API route handlers run on the runtime, prerendered pages are served as static files, and the platform services are available in both.
Install
npm install -D @maravilla-labs/adapter-astro
npm install @maravilla-labs/platform
Configure
Add the adapter to your astro.config.mjs. Astro’s standard build then produces the .maravilla/ bundle — there is no separate build command:
// astro.config.mjs
import { defineConfig } from 'astro/config';
import maravilla from '@maravilla-labs/adapter-astro';
export default defineConfig({
output: 'server',
adapter: maravilla(),
});
Then build and run it locally:
astro build
maravilla preview .maravilla
Platform services
Platform bindings are available on Astro.locals in any server-rendered page:
---
// src/pages/index.astro — server-rendered on every request
const { platform } = Astro.locals;
const visits = await platform.env.KV.demo.get('visits');
const notes = await platform.env.DB.find('notes', {});
---
<h1>Astro on Maravilla</h1>
<p>Visits: {visits ?? 0}</p>
<ul>
{notes.map((note) => <li>{note.text}</li>)}
</ul>
API route handlers receive the same locals:
// src/pages/api/notes.ts
import type { APIRoute } from 'astro';
export const GET: APIRoute = async ({ locals }) => {
const notes = await locals.platform.env.DB.find('notes', {});
return Response.json({ notes });
};
export const POST: APIRoute = async ({ locals, request }) => {
const { text } = await request.json();
const result = await locals.platform.env.DB.insertOne('notes', {
text,
createdAt: new Date().toISOString(),
});
return Response.json({ inserted: result }, { status: 201 });
};
Object storage and queues are reached the same way, via locals.platform.env.STORAGE and locals.platform.env.QUEUE.
Types for Astro.locals are declared in src/env.d.ts:
/// <reference types="astro/client" />
declare namespace App {
// Locals injected by @maravilla-labs/adapter-astro
interface Locals {
platform: any;
env: Record<string, any>;
kv: any;
db: any;
storage: any;
queue: any;
}
}
Prerendered pages
Pages that opt into prerendering are rendered at build time and served as static files — they never hit the server on a request:
---
// src/pages/about.astro
export const prerender = true;
const builtAt = new Date().toISOString();
---
<h1>About</h1>
<p>Prerendered at build time: <code>{builtAt}</code></p>
Mix the two freely: keep the marketing pages prerendered and let the dynamic routes render per request.
Sessions
Astro.session works with no extra configuration. The adapter wires a session store backed by the platform key-value service, so session data survives across requests and instances:
// src/pages/api/session.ts
import type { APIRoute } from 'astro';
export const GET: APIRoute = async ({ session }) => {
const count = ((await session.get('count')) ?? 0) + 1;
session.set('count', count);
return Response.json({ count });
};
Adapter options let you change the key-value namespace or opt out entirely:
maravilla({ sessionNamespace: 'my_sessions' }) // custom KV namespace
maravilla({ sessionDriver: false }) // opt out of the default store
A session.driver you configure yourself in the Astro config always wins.
Local development
maravilla dev runs your Astro dev server alongside the platform services, so the key-value store, database, object storage and queues are available while you work:
maravilla dev
Adapter options
| Option | Type | Default | Description |
|---|---|---|---|
out | string | '.maravilla' | Output directory for the build |
sessionDriver | boolean | true | Wire the key-value store for Astro.session |
sessionNamespace | string | 'astro_sessions' | Key-value namespace used for session data |
envPrefix | string | 'PUBLIC_' | Prefix for client-exposed environment variables |
include / exclude | string[] | ['/*'] / [] | Route include/exclude patterns |
external | string[] | [] | Dependencies to exclude from the server bundle |
polyfill | boolean | true | Include runtime polyfills |
precompress | boolean | false | Pre-compress static assets with gzip/brotli |
Images
Astro’s built-in image optimization does not run at request time; images are served as authored. For resizing, cropping and format conversion, use the platform media transforms.
Production readiness
Treat this integration as experimental: pin the adapter version, expect option names and defaults to move between releases, and re-test after upgrading. For workloads you can’t afford to revisit, SvelteKit, React Router and the Nitro-based frameworks are the settled choices.
Nitro-based frameworks (SolidStart, TanStack Start)
Frameworks built on Nitro are supported through @maravilla-labs/preset-nitro. This includes SolidStart and TanStack Start.
Install
npm install -D @maravilla-labs/preset-nitro
npm install @maravilla-labs/platform
SolidStart
Configure the Nitro preset in your app.config.ts:
import { defineConfig } from '@solidjs/start/config';
export default defineConfig({
server: {
preset: '@maravilla-labs/preset-nitro'
}
});
TanStack Start
Configure the Nitro preset in your app.config.ts:
import { defineConfig } from '@tanstack/start/config';
export default defineConfig({
server: {
preset: '@maravilla-labs/preset-nitro'
}
});
Standalone Nitro
You can also use the preset directly in a Nitro config:
// nitro.config.ts
export default defineNitroConfig({
preset: '@maravilla-labs/preset-nitro'
});
The preset automatically detects which framework is in use (SolidStart, TanStack Start, or plain Nitro) and configures the build accordingly.
What About Next.js?
Next.js is not currently supported. The framework has historically been deeply coupled to Vercel’s proprietary infrastructure, making it impractical to run on alternative runtimes without relying on undocumented internals that break between releases.
Next.js 16.2 (March 2026) introduced a stable Adapter API, and the OpenNext working group — with Cloudflare, Netlify, AWS, and Google — is building verified adapters for non-Vercel platforms. This is a promising step toward true portability.
We are monitoring the Adapter API as it matures. Once the ecosystem stabilizes and verified adapters prove reliable in production, we will re-evaluate adding Next.js support. In the meantime, React Router provides a fully supported alternative for React-based applications on Maravilla.
Platform services across frameworks
Regardless of framework, use @maravilla-labs/platform to access platform services in server-side code:
npm install @maravilla-labs/platform
The platform client auto-detects whether it is running in development (connecting to the local platform dev server on port 3001) or production (using the runtime’s built-in services). Import and use it in any server-side handler:
import { getPlatform } from '@maravilla-labs/platform';
const platform = getPlatform();
// KV Store -- key-value storage with optional TTL
await platform.KV.demo.put('key', 'value', { expirationTtl: 3600 });
const value = await platform.KV.demo.get('key');
// Database -- document document operations
await platform.DB.insertOne('users', { name: 'Alice', active: true });
const users = await platform.DB.find('users', { active: true }, {
sort: { createdAt: -1 },
limit: 20
});
CI/CD pipeline
All frameworks use the same pipeline format. Place a pipeline.yml in your .maravilla/ directory:
name: build-and-deploy
image: node:22-alpine
on:
push:
branches: [main]
jobs:
build:
steps:
- name: Install dependencies
commands:
- npm ci
- name: Build app
commands:
- npm run build
artifacts:
paths: [build/]
resources:
cpu: "2000m"
memory: "4Gi"
Next steps
- Getting Started — quick start walkthrough
- Installation — CLI installation and authentication