Fix: Actions Require Server Output in Astro

Error message:
Actions are not enabled because there is no server output configured.
Actions 2025-01-25

What Causes This Error?

This error occurs when you try to use Astro Actions without configuring server-side rendering (SSR) output. Actions require a server to execute and can’t work with static site generation.

The Problem

// astro.config.mjs
export default defineConfig({
  // output: 'static' (default) - no server!
});
// src/actions/index.ts
import { defineAction, z } from 'astro:actions';

export const server = {
  // ❌ Actions require server output
  subscribe: defineAction({
    input: z.object({ email: z.string().email() }),
    handler: async ({ email }) => {
      // Handle subscription
    },
  }),
};

The Fix

Enable Server Output

// astro.config.mjs
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';

export default defineConfig({
  output: 'server',  // ✅ Enable SSR
  adapter: node({
    mode: 'standalone',
  }),
});

Common Scenarios

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

export default defineConfig({
  output: 'hybrid',  // Static by default, SSR when needed
  adapter: vercel(),
});
---
// Pages are static unless prerender is false
export const prerender = false;  // This page uses SSR
---

Basic Action Setup

// src/actions/index.ts
import { defineAction, z } from 'astro:actions';

export const server = {
  newsletter: defineAction({
    input: z.object({
      email: z.string().email(),
      name: z.string().optional(),
    }),
    handler: async ({ email, name }) => {
      // Store in database, send to API, etc.
      await saveSubscriber(email, name);
      return { success: true };
    },
  }),
};

Calling Actions from Client

---
// src/pages/subscribe.astro
export const prerender = false;
---

<html>
<body>
  <form id="subscribe-form">
    <input type="email" name="email" required />
    <button type="submit">Subscribe</button>
  </form>

  <script>
    import { actions } from 'astro:actions';

    document.getElementById('subscribe-form')
      ?.addEventListener('submit', async (e) => {
        e.preventDefault();
        const formData = new FormData(e.target);

        const result = await actions.newsletter({
          email: formData.get('email'),
        });

        if (result.data?.success) {
          alert('Subscribed!');
        }
      });
  </script>
</body>
</html>

Different Adapters

// Vercel
import vercel from '@astrojs/vercel';
export default defineConfig({
  output: 'server',
  adapter: vercel(),
});

// Netlify
import netlify from '@astrojs/netlify';
export default defineConfig({
  output: 'server',
  adapter: netlify(),
});

// Cloudflare
import cloudflare from '@astrojs/cloudflare';
export default defineConfig({
  output: 'server',
  adapter: cloudflare(),
});

// Node.js
import node from '@astrojs/node';
export default defineConfig({
  output: 'server',
  adapter: node({ mode: 'standalone' }),
});

Form Actions (No JavaScript)

---
import { actions } from 'astro:actions';
export const prerender = false;

// Handle form submission on server
const result = Astro.getActionResult(actions.newsletter);
---

<form method="POST" action={actions.newsletter}>
  <input type="email" name="email" required />
  <button type="submit">Subscribe</button>
</form>

{result?.error && <p>Error: {result.error.message}</p>}
{result?.data?.success && <p>Subscribed successfully!</p>}

Check Output Mode

// Verify your output setting
export default defineConfig({
  output: 'server',   // Required for actions
  // or
  output: 'hybrid',   // Also works

  // NOT: output: 'static' (default)
});

Quick Checklist

  • Set output: 'server' or output: 'hybrid'
  • Install and configure an SSR adapter
  • Define actions in src/actions/index.ts
  • Use prerender: false for pages calling actions
  • Import actions from astro:actions