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

OptionTypeDefaultDescription
outstring'.maravilla'Output directory for the build
precompressbooleanfalsePre-compress static assets with gzip/brotli
polyfillbooleantrueInclude runtime polyfills
envPrefixstring'PUBLIC_'Prefix for client-exposed environment variables
include / excludestring[]['/*'] / []Route include/exclude patterns
externalstring[][]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

OptionTypeDefaultDescription
outstring'.maravilla'Output directory
serverBuildPathstring'build/server'Path to React Router server build
clientBuildPathstring'build/client'Path to React Router client build
serverBuildFilestring'index.js'Server build entry file name
envPrefixstring'PUBLIC_'Prefix for client-exposed environment variables
polyfillbooleantrueInclude runtime polyfills
precompressbooleanfalsePre-compress static assets
externalstring[][]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

OptionTypeDefaultDescription
outstring'.maravilla'Output directory for the build
sessionDriverbooleantrueWire the key-value store for Astro.session
sessionNamespacestring'astro_sessions'Key-value namespace used for session data
envPrefixstring'PUBLIC_'Prefix for client-exposed environment variables
include / excludestring[]['/*'] / []Route include/exclude patterns
externalstring[][]Dependencies to exclude from the server bundle
polyfillbooleantrueInclude runtime polyfills
precompressbooleanfalsePre-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