DevToolBoxGRATIS
Blog

Astro Guide 2026: Islands Architecture, Content Collections, SSR & View Transitions

19 min readby DevToolBox

Astro is a modern web framework designed for content-driven websites that ships zero JavaScript by default. With its unique islands architecture, Astro renders pages to static HTML at build time while allowing interactive components to hydrate independently. Whether you are building a blog, documentation site, marketing page, or e-commerce storefront, Astro delivers exceptional performance with a developer experience that supports React, Vue, Svelte, and Solid components in a single project.

TL;DR

Astro is a content-first web framework that ships zero JS by default, uses islands architecture for selective interactivity, supports multiple UI frameworks (React, Vue, Svelte), and delivers outstanding Core Web Vitals scores. It features content collections for type-safe data, view transitions for smooth navigation, SSR and SSG modes, and integrates seamlessly with Tailwind, MDX, and image optimization.

Key Takeaways
  • Astro ships zero JavaScript by default, resulting in faster page loads and better Core Web Vitals than traditional SPA frameworks.
  • Islands architecture allows mixing React, Vue, Svelte, and Solid components on the same page, each hydrating independently.
  • Content Collections provide type-safe access to Markdown, MDX, YAML, and JSON data with automatic schema validation using Zod.
  • View Transitions API enables smooth page animations without a client-side router or JavaScript framework.
  • Astro supports both static site generation (SSG) and on-demand server-side rendering (SSR) with adapters for Vercel, Netlify, and Cloudflare.
  • Astro DB provides a built-in SQL database powered by LibSQL for structured data with full TypeScript type safety.

What Is Astro and How Does It Work?

Astro is a web framework that takes a fundamentally different approach from React-based frameworks like Next.js or Remix. Instead of building a JavaScript application that renders HTML, Astro builds an HTML website that can optionally include JavaScript. Pages are rendered to static HTML at build time by default, and interactive components are loaded only where needed using a technique called islands architecture.

Astro uses a component format (.astro files) that combines a frontmatter script section with an HTML template. The frontmatter runs at build time (or request time in SSR mode) and never reaches the browser. This means you can safely import Node.js modules, query databases, and fetch APIs directly in your components.

---
// src/pages/index.astro
import Layout from "../layouts/Layout.astro";
import Card from "../components/Card.astro";
import Counter from "../components/Counter.tsx";

// This runs at build time (or request time in SSR)
// It never reaches the browser
const response = await fetch("https://api.example.com/posts");
const posts = await response.json();
---

<Layout title="My Blog">
  <h1>Latest Posts</h1>

  <!-- Static HTML: zero JavaScript -->
  {posts.map((post) => (
    <Card title={post.title} url={"/blog/" + post.slug} />
  ))}

  <!-- Interactive island: only this loads JS -->
  <Counter client:visible initialCount={0} />
</Layout>

Islands Architecture Explained

The islands architecture is the core innovation that makes Astro unique. In a traditional SPA, the entire page is a single JavaScript application. In Astro, the page is static HTML with interactive "islands" that hydrate independently. Each island is a self-contained widget that loads its own JavaScript bundle.

You control when and how each island hydrates using client directives. This means a page with a static header, a dynamic search bar, and an interactive chart only loads JavaScript for the search bar and chart. The header remains pure HTML with zero JavaScript overhead.

Client Hydration Directives

Astro provides several client directives to control island hydration timing:

  • client:load - Hydrate immediately on page load. Use for above-the-fold interactive elements.
  • client:idle - Hydrate once the browser is idle (requestIdleCallback). Use for lower-priority interactive elements.
  • client:visible - Hydrate when the element enters the viewport (IntersectionObserver). Use for below-the-fold elements.
  • client:media - Hydrate when a CSS media query matches. Use for mobile-only or desktop-only interactivity.
  • client:only - Skip server rendering and only render on the client. Use for components that depend on browser APIs.
---
// src/pages/dashboard.astro
import Header from "../components/Header.astro";
import SearchBar from "../components/SearchBar.tsx";
import Chart from "../components/Chart.svelte";
import Newsletter from "../components/Newsletter.vue";
---

<!-- Pure HTML, zero JS -->
<Header />

<!-- Hydrate immediately for above-the-fold search -->
<SearchBar client:load placeholder="Search tools..." />

<!-- Hydrate when visible (below the fold) -->
<Chart client:visible data={chartData} />

<!-- Hydrate when idle (low priority) -->
<Newsletter client:idle />

<!-- Only hydrate on mobile screens -->
<MobileMenu client:media="(max-width: 768px)" />

<!-- Client-only: skip SSR for browser-API-dependent code -->
<MapWidget client:only="react" />

Content Collections and Type Safety

Content Collections are one of Astro most powerful features. They provide a structured way to organize, validate, and query your content with full TypeScript type safety. Collections live in the src/content directory and are defined with a schema using Zod validation.

When you define a collection schema, Astro generates TypeScript types automatically. If a Markdown frontmatter field is missing or has the wrong type, you get a build-time error instead of a runtime surprise. This is especially valuable for blogs with hundreds of posts.

// src/content.config.ts
import { defineCollection, z } from "astro:content";
import { glob } from "astro/loaders";

const blog = defineCollection({
  loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/data/blog" }),
  schema: z.object({
    title: z.string(),
    description: z.string(),
    pubDate: z.coerce.date(),
    updatedDate: z.coerce.date().optional(),
    heroImage: z.string().optional(),
    tags: z.array(z.string()).default([]),
    draft: z.boolean().default(false),
    author: z.string().default("Anonymous"),
  }),
});

const docs = defineCollection({
  loader: glob({ pattern: "**/*.md", base: "./src/data/docs" }),
  schema: z.object({
    title: z.string(),
    section: z.enum(["getting-started", "guides", "reference"]),
    order: z.number(),
  }),
});

export const collections = { blog, docs };
---
// src/pages/blog/[slug].astro
import { getCollection, getEntry, render } from "astro:content";
import Layout from "../../layouts/Layout.astro";

// Generate static pages for all non-draft blog posts
export async function getStaticPaths() {
  const posts = await getCollection("blog", ({ data }) => {
    return data.draft !== true;
  });
  return posts.map((post) => ({
    params: { slug: post.id },
    props: { post },
  }));
}

const { post } = Astro.props;
const { Content, headings } = await render(post);
---

<Layout title={post.data.title}>
  <article>
    <h1>{post.data.title}</h1>
    <p>{post.data.description}</p>
    <time datetime={post.data.pubDate.toISOString()}>
      {post.data.pubDate.toLocaleDateString()}
    </time>
    <div class="tags">
      {post.data.tags.map((tag) => <span>{tag}</span>)}
    </div>
    <Content />
  </article>
</Layout>

Astro Components vs Framework Components

Astro has its own component format (.astro files) that is specifically optimized for building HTML output. However, you can also use components from React, Vue, Svelte, Solid, Preact, and Lit within Astro pages. Understanding when to use each is key to getting the best performance.

Astro Components (.astro)

Astro components are HTML-first templates with a JavaScript frontmatter section. They render entirely at build time and produce zero JavaScript output. Use them for layouts, page structures, and any non-interactive UI.

---
// src/components/Card.astro
interface Props {
  title: string;
  description: string;
  url: string;
  tags?: string[];
}

const { title, description, url, tags = [] } = Astro.props;
---

<a href={url} class="card">
  <h3>{title}</h3>
  <p>{description}</p>
  {tags.length > 0 && (
    <div class="tags">
      {tags.map((tag) => <span class="tag">{tag}</span>)}
    </div>
  )}
</a>

<style>
  .card {
    display: block;
    padding: 1.5rem;
    border: 1px solid #e2e8f0;
    border-radius: 0.5rem;
    text-decoration: none;
    transition: box-shadow 0.2s;
  }
  .card:hover {
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  }
</style>

Framework Components (React, Vue, Svelte)

Use framework components when you need client-side interactivity. Astro renders them to HTML on the server and then hydrates them on the client when you add a client directive. You can mix multiple frameworks on the same page.

// src/components/Counter.tsx (React island)
import { useState } from "react";

export default function Counter({ initialCount = 0 }) {
  const [count, setCount] = useState(initialCount);

  return (
    <div>
      <button onClick={() => setCount(count - 1)}>-</button>
      <span>{count}</span>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  );
}
<!-- src/components/Toggle.svelte (Svelte island) -->
<script>
  let isOpen = false;
</script>

<button on:click={() => isOpen = !isOpen}>
  {isOpen ? "Close" : "Open"}
</button>

{#if isOpen}
  <div class="panel">
    <slot />
  </div>
{/if}
---
// Using multiple frameworks on one page
import Counter from "../components/Counter.tsx";      // React
import Toggle from "../components/Toggle.svelte";     // Svelte
import Carousel from "../components/Carousel.vue";    // Vue
---

<Counter client:load initialCount={5} />
<Toggle client:visible>Content inside Svelte slot</Toggle>
<Carousel client:idle images={images} />

Static Site Generation and Server-Side Rendering

Astro supports two primary output modes: static (default) and server. You can also use hybrid mode to mix static and server-rendered pages in the same project.

Static Mode (Default)

In static mode, Astro pre-renders every page to HTML at build time. This produces a directory of HTML files that can be deployed to any static hosting provider. Static sites are the fastest and most reliable option.

// astro.config.mjs - Static mode (default)
import { defineConfig } from "astro/config";

export default defineConfig({
  output: "static",  // This is the default
  site: "https://example.com",
});

Server Mode (SSR)

In server mode, pages are rendered on the server for each request. This enables dynamic features like user authentication, personalized content, and database queries at request time. You need a server adapter (Vercel, Netlify, Cloudflare, Node.js) to deploy SSR sites.

// astro.config.mjs - Server mode
import { defineConfig } from "astro/config";
import vercel from "@astrojs/vercel";

export default defineConfig({
  output: "server",
  adapter: vercel(),
});

Hybrid Mode

Hybrid mode lets you pre-render most pages statically while opting specific pages into server rendering. This is ideal for sites where most content is static but a few pages need dynamic data.

// astro.config.mjs - Hybrid mode
import { defineConfig } from "astro/config";
import netlify from "@astrojs/netlify";

export default defineConfig({
  output: "hybrid",
  adapter: netlify(),
});

// --- src/pages/about.astro (pre-rendered at build time) ---
// Pages are static by default in hybrid mode
---
<h1>About Us</h1>
<p>This page is pre-rendered at build time.</p>
---

// --- src/pages/dashboard.astro (server-rendered) ---
// Opt into server rendering for this page
export const prerender = false;
---
const user = await getUser(Astro.cookies.get("session"));
---
<h1>Welcome, {user.name}</h1>

View Transitions and Page Animations

Astro includes built-in support for the View Transitions API, enabling smooth animated page transitions without a client-side router. When a user navigates between pages, Astro intercepts the navigation and applies CSS-based transitions for a seamless experience.

You can customize transitions per element, persist state across navigations, and even animate elements between pages using the transition:name directive. This gives you SPA-like navigation without the JavaScript overhead of a full client-side router.

---
// src/layouts/Layout.astro
import { ViewTransitions } from "astro:transitions";
---

<html>
  <head>
    <ViewTransitions />
  </head>
  <body>
    <nav transition:persist>
      <!-- Nav persists across navigations -->
      <a href="/">Home</a>
      <a href="/blog">Blog</a>
    </nav>
    <main transition:animate="slide">
      <slot />
    </main>
  </body>
</html>
---
// Custom transitions with transition:name
// Blog listing page
---
{posts.map((post) => (
  <a href={"/blog/" + post.slug}>
    <img
      src={post.image}
      transition:name={"hero-" + post.slug}
    />
    <h2 transition:name={"title-" + post.slug}>
      {post.title}
    </h2>
  </a>
))}

// --- Blog detail page ---
// Same transition:name creates a shared element transition
<img
  src={post.image}
  transition:name={"hero-" + post.slug}
/>
<h1 transition:name={"title-" + post.slug}>
  {post.title}
</h1>

Astro DB and the Data Layer

Astro DB is a built-in SQL database designed for content sites. Powered by LibSQL (a fork of SQLite), it provides a fully typed database with automatic migrations and a simple query API. Astro DB runs locally during development and can be deployed to Astro Studio for production.

The data layer extends content collections with support for any data source. You can define loaders that pull data from external APIs, CMS platforms, or databases and make it available through the same type-safe content collections API.

// db/config.ts - Define your database schema
import { defineDb, defineTable, column } from "astro:db";

const Comment = defineTable({
  columns: {
    id: column.number({ primaryKey: true }),
    postSlug: column.text(),
    author: column.text(),
    body: column.text(),
    createdAt: column.date({ default: "NOW" }),
  },
});

const Like = defineTable({
  columns: {
    id: column.number({ primaryKey: true }),
    postSlug: column.text(),
    count: column.number({ default: 0 }),
  },
});

export default defineDb({
  tables: { Comment, Like },
});
---
// Querying Astro DB in a page
import { db, Comment, eq } from "astro:db";

const { slug } = Astro.params;
const comments = await db
  .select()
  .from(Comment)
  .where(eq(Comment.postSlug, slug))
  .orderBy(Comment.createdAt);
---

<h2>Comments</h2>
{comments.map((c) => (
  <div class="comment">
    <strong>{c.author}</strong>
    <p>{c.body}</p>
    <time>{c.createdAt.toLocaleDateString()}</time>
  </div>
))}

Middleware and API Endpoints

Astro supports middleware for intercepting requests before they reach your pages. Middleware runs on the server and can modify the request context, add authentication checks, redirect users, or set response headers.

// src/middleware.ts
import { defineMiddleware, sequence } from "astro:middleware";

const auth = defineMiddleware(async (context, next) => {
  const token = context.cookies.get("session")?.value;

  if (context.url.pathname.startsWith("/dashboard")) {
    if (!token) {
      return context.redirect("/login");
    }
    const user = await verifyToken(token);
    context.locals.user = user;
  }

  return next();
});

const logging = defineMiddleware(async (context, next) => {
  const start = Date.now();
  const response = await next();
  const duration = Date.now() - start;
  console.log(context.url.pathname + " - " + duration + "ms");
  return response;
});

export const onRequest = sequence(logging, auth);

API Endpoints

Astro can serve JSON API endpoints alongside your pages. Create .ts or .js files in the src/pages directory that export HTTP method handlers (GET, POST, PUT, DELETE). This is useful for form submissions, webhooks, and building lightweight APIs.

// src/pages/api/comments.ts
import type { APIRoute } from "astro";
import { db, Comment } from "astro:db";

export const GET: APIRoute = async ({ url }) => {
  const slug = url.searchParams.get("slug");
  if (!slug) {
    return new Response(
      JSON.stringify({ error: "slug is required" }),
      { status: 400 }
    );
  }

  const comments = await db
    .select()
    .from(Comment)
    .where(eq(Comment.postSlug, slug));

  return new Response(JSON.stringify(comments), {
    headers: { "Content-Type": "application/json" },
  });
};

export const POST: APIRoute = async ({ request }) => {
  const body = await request.json();
  const { postSlug, author, content } = body;

  await db.insert(Comment).values({
    postSlug,
    author,
    body: content,
  });

  return new Response(
    JSON.stringify({ success: true }),
    { status: 201 }
  );
};

Integrations: Tailwind, MDX, and Image Optimization

Astro has a rich ecosystem of official and community integrations. The most commonly used integrations include Tailwind CSS for styling, MDX for interactive Markdown, and the built-in image optimization service.

Tailwind CSS Integration

Astro has first-class Tailwind CSS support. The integration handles configuration, PostCSS setup, and purging unused styles automatically.

# Install Tailwind integration
npx astro add tailwind

# This automatically:
# 1. Installs @astrojs/tailwind and tailwindcss
# 2. Adds the integration to astro.config.mjs
# 3. Creates a tailwind.config.mjs file
// astro.config.mjs
import { defineConfig } from "astro/config";
import tailwind from "@astrojs/tailwind";

export default defineConfig({
  integrations: [
    tailwind({
      // Apply base styles automatically
      applyBaseStyles: true,
      // Path to custom config
      configFile: "./tailwind.config.mjs",
    }),
  ],
});

MDX Integration

MDX lets you use JSX components inside Markdown files. With the MDX integration, you can embed interactive components, charts, and custom layouts within your content.

# Install MDX integration
npx astro add mdx

# Now you can use .mdx files in content collections
# and import components directly in Markdown
---
// src/data/blog/interactive-post.mdx
title: "Interactive Tutorial"
pubDate: 2026-01-15
---
import CodePlayground from "../../components/CodePlayground.tsx";
import Chart from "../../components/Chart.svelte";

# Interactive Tutorial

Here is a live code playground:

<CodePlayground client:visible code="console.log(42)" />

And a dynamic chart:

<Chart client:idle type="bar" data={[10, 20, 30]} />

Image Optimization

Astro includes a built-in image optimization service that automatically resizes, converts, and optimizes images. The Image component generates responsive srcset attributes and supports lazy loading out of the box.

---
// Using the Image component
import { Image } from "astro:assets";
import heroImage from "../assets/hero.png";
---

<!-- Automatic optimization: resize, format, lazy load -->
<Image
  src={heroImage}
  alt="Hero banner"
  width={1200}
  height={600}
  format="webp"
  quality={80}
/>

<!-- Remote images with dimensions -->
<Image
  src="https://example.com/photo.jpg"
  alt="Remote photo"
  width={800}
  height={400}
  inferSize
/>

<!-- Picture component for art direction -->
import { Picture } from "astro:assets";

<Picture
  src={heroImage}
  formats={["avif", "webp"]}
  alt="Responsive hero"
  widths={[400, 800, 1200]}
  sizes="(max-width: 800px) 100vw, 800px"
/>

Deployment to Vercel, Netlify, and Cloudflare

Astro supports deployment to every major hosting platform through official adapters. Each adapter handles the platform-specific configuration for SSR, serverless functions, and edge runtime.

Deploying to Vercel

# Install the Vercel adapter
npx astro add vercel

# astro.config.mjs
import { defineConfig } from "astro/config";
import vercel from "@astrojs/vercel";

export default defineConfig({
  output: "server",  // or "hybrid"
  adapter: vercel({
    // Enable ISR with 60-second revalidation
    isr: {
      expiration: 60,
    },
    // Enable image optimization
    imageService: true,
    // Enable Web Analytics
    webAnalytics: { enabled: true },
  }),
});

Deploying to Netlify

# Install the Netlify adapter
npx astro add netlify

# astro.config.mjs
import { defineConfig } from "astro/config";
import netlify from "@astrojs/netlify";

export default defineConfig({
  output: "server",
  adapter: netlify({
    // Use edge functions for faster cold starts
    edgeMiddleware: true,
    // Cache static assets
    cacheOnDemandPages: true,
  }),
});

Deploying to Cloudflare Pages

# Install the Cloudflare adapter
npx astro add cloudflare

# astro.config.mjs
import { defineConfig } from "astro/config";
import cloudflare from "@astrojs/cloudflare";

export default defineConfig({
  output: "server",
  adapter: cloudflare({
    mode: "directory",
    // Access Cloudflare bindings (KV, D1, R2)
    platformProxy: {
      enabled: true,
    },
  }),
});

// Access Cloudflare bindings in your pages
// src/pages/api/data.ts
export const GET: APIRoute = async ({ locals }) => {
  const { runtime } = locals;
  const kv = runtime.env.MY_KV_NAMESPACE;
  const value = await kv.get("key");
  return new Response(JSON.stringify({ value }));
};

Performance Optimization and Best Practices

Astro delivers excellent performance out of the box, but there are several techniques to optimize further.

  • Use client:visible instead of client:load for below-the-fold interactive components to defer JavaScript loading.
  • Leverage content collections for type-safe content with build-time validation instead of runtime data fetching.
  • Use the built-in Image component for automatic image optimization with responsive srcset generation.
  • Enable view transitions for smooth navigation without loading a client-side router framework.
  • Prefer Astro components over framework components for non-interactive UI to eliminate JavaScript overhead.
  • Use hybrid rendering mode to pre-render static pages while keeping dynamic pages server-rendered.
  • Split large interactive components into smaller islands to reduce individual bundle sizes.
  • Configure prefetching for anticipated navigation to improve perceived performance.
  • Use Astro built-in asset handling for fonts, scripts, and stylesheets to benefit from automatic hashing and caching.
  • Profile your build output with the astro build --profile flag to identify large bundles and optimization opportunities.
// Performance-optimized astro.config.mjs
import { defineConfig } from "astro/config";
import tailwind from "@astrojs/tailwind";
import compress from "astro-compress";
import sitemap from "@astrojs/sitemap";

export default defineConfig({
  site: "https://example.com",
  integrations: [
    tailwind(),
    sitemap(),
    compress({
      CSS: true,
      HTML: true,
      JavaScript: true,
      Image: true,
      SVG: true,
    }),
  ],
  prefetch: {
    prefetchAll: false,
    defaultStrategy: "viewport",
  },
  image: {
    service: { entrypoint: "astro/assets/services/sharp" },
    remotePatterns: [
      { protocol: "https", hostname: "**.example.com" },
    ],
  },
  vite: {
    build: {
      cssMinify: "lightningcss",
      rollupOptions: {
        output: {
          manualChunks: {
            react: ["react", "react-dom"],
          },
        },
      },
    },
  },
});

Frequently Asked Questions

What is Astro used for?

Astro is primarily used for content-driven websites including blogs, documentation sites, marketing pages, portfolios, and e-commerce storefronts. Its zero-JavaScript-by-default approach makes it ideal for sites where performance and SEO are critical. Astro can also handle full-stack applications with its SSR mode and API endpoints.

How does Astro islands architecture work?

Astro renders every page to static HTML by default. When you need interactivity, you add a client directive (client:load, client:idle, client:visible) to a framework component. That component becomes an independent island that hydrates separately from the rest of the page. Each island loads its own JavaScript bundle, so a page with one interactive widget only loads JavaScript for that widget.

Can I use React components in Astro?

Yes. Astro supports React, Vue, Svelte, Solid, Preact, and Lit components. You can even mix multiple frameworks on the same page. Install the framework integration (e.g., @astrojs/react) and use client directives to control hydration. Astro handles server-rendering and client-side hydration automatically.

Is Astro better than Next.js?

Astro and Next.js serve different primary use cases. Astro excels at content-heavy websites where performance and minimal JavaScript are priorities. Next.js is better for complex interactive web applications that need full-stack React features. For blogs, docs, and marketing sites, Astro typically delivers faster page loads and better Core Web Vitals.

How do Astro content collections work?

Content collections organize your Markdown, MDX, YAML, and JSON files in the src/content directory. You define a schema using Zod for each collection, and Astro generates TypeScript types automatically. This gives you build-time validation of frontmatter fields, type-safe querying, and autocompletion in your editor.

Does Astro support server-side rendering?

Yes. Astro supports both static site generation (SSG) and server-side rendering (SSR). Use the server or hybrid output mode and install an adapter for your deployment platform (Vercel, Netlify, Cloudflare, or Node.js). In hybrid mode, you can mix static and server-rendered pages in the same project.

What are Astro view transitions?

Astro view transitions use the browser View Transitions API to animate page navigations. When enabled, Astro intercepts link clicks and applies CSS transitions between the old and new page. You can customize transitions per element, persist component state across navigations, and create complex multi-element animations.

How do I deploy an Astro site?

For static sites, build with astro build and deploy the dist directory to any static host (GitHub Pages, Vercel, Netlify, Cloudflare Pages). For SSR sites, install the appropriate adapter (@astrojs/vercel, @astrojs/netlify, @astrojs/cloudflare, @astrojs/node) and deploy to that platform. Most platforms auto-detect Astro projects.

𝕏 Twitterin LinkedIn
Was dit nuttig?

Blijf op de hoogte

Ontvang wekelijkse dev-tips en nieuwe tools.

Geen spam. Altijd opzegbaar.

Try These Related Tools

{ }JSON FormatterMDMarkdown to HTML

Related Articles

Next.js Advanced Guide: App Router, Server Components, Data Fetching, Middleware & Performance

Complete Next.js advanced guide covering App Router architecture, React Server Components, streaming SSR, data fetching patterns, middleware, route handlers, parallel and intercepting routes, caching strategies, ISR, image optimization, and deployment best practices.

Web Performance Optimalisatie: Core Web Vitals Gids 2026

Complete gids voor web performance optimalisatie en Core Web Vitals. Verbeter LCP, INP en CLS met praktische technieken voor afbeeldingen, JavaScript, CSS en caching.

Tailwind CSS Advanced Guide: v4 Features, Custom Plugins, Dark Mode, CVA, Animation & Performance

Complete Tailwind CSS advanced guide covering v4 new features, design systems, custom plugins, responsive design, dark mode, animations, CVA component patterns, React integration, performance optimization, arbitrary values, grid layouts, and migration from v3 to v4.