On this page
Server Mode (SSR)
shipyard fully supports Astro’s server-side rendering (SSR) mode, allowing you to render pages on-demand per request instead of at build time.
When to Use Server Mode
Server mode is ideal for:
- Dynamic content that changes frequently
- User authentication and personalized pages
- Database-driven content that needs real-time updates
- Applications requiring request-time data
For static content like documentation and blog posts, we recommend using static mode (the default) for better performance and simpler deployment.
Configuration
Step 1: Install an Adapter
Astro requires an adapter for server-side rendering. Choose one based on your deployment platform:
# For Cloudflare Workers/Pages
npm install @astrojs/cloudflare
# For Node.js
npm install @astrojs/node
# For Vercel
npm install @astrojs/vercel
# For Netlify
npm install @astrojs/netlify
Step 2: Configure Astro
Update your astro.config.mjs to enable server mode:
import cloudflare from '@astrojs/cloudflare' // or your chosen adapter
import tailwind from '@astrojs/tailwind'
import shipyard from '@levino/shipyard-base'
import shipyardDocs from '@levino/shipyard-docs'
import shipyardBlog from '@levino/shipyard-blog'
import { defineConfig } from 'astro/config'
export default defineConfig({
output: 'server', // Enable server mode
adapter: cloudflare(), // Your chosen adapter
integrations: [
tailwind({ applyBaseStyles: false }),
shipyard({
brand: 'My Site',
title: 'My Site',
tagline: 'Built with shipyard',
navigation: {
docs: { label: 'Docs', href: '/docs' },
blog: { label: 'Blog', href: '/blog' },
},
}),
shipyardDocs(),
shipyardBlog(),
],
})
How It Works
When using server mode:
- Documentation and blog pages are rendered on-demand when requested
- Custom pages in
src/pages/can include dynamic server-side logic - Static assets are still served from the build output
Example: Dynamic Custom Page
You can create dynamic pages that fetch data at request time:
---
// src/pages/dashboard.astro
import { Page } from '@levino/shipyard-base/layouts'
// This runs on every request
const userData = await fetchUserData()
---
<Page title="Dashboard" description="Your personal dashboard">
<h1>Welcome, {userData.name}!</h1>
<p>Last login: {userData.lastLogin}</p>
</Page>
Hybrid Rendering
For optimal performance, you can mix static and server-rendered pages using Astro’s hybrid mode:
export default defineConfig({
output: 'hybrid', // Enable hybrid mode
adapter: cloudflare(),
// ...
})
In hybrid mode, pages are static by default. Add export const prerender = false to any page that needs server-side rendering:
---
// src/pages/api-data.astro
export const prerender = false
const data = await fetch('https://api.example.com/data')
---
Server Mode with Auth Middleware (Express)
A common pattern is to wrap Astro’s Node.js server behind an Express app that handles authentication (e.g. Passport.js, session stores). Here’s how to set that up with shipyard.
Configuration
When docs sit behind authentication middleware, pages must be rendered on every request so the middleware can check credentials. Set prerender: false explicitly:
import node from '@astrojs/node'
import shipyard from '@levino/shipyard-base'
import shipyardDocs from '@levino/shipyard-docs'
import tailwindcss from '@tailwindcss/vite'
import { defineConfig } from 'astro/config'
import appCss from './src/styles/app.css?url'
export default defineConfig({
output: 'server',
adapter: node({ mode: 'middleware' }),
vite: {
plugins: [tailwindcss()],
},
integrations: [
shipyard({
css: appCss,
brand: 'Internal Docs',
title: 'Internal Docs',
navigation: {
docs: { label: 'Docs', href: '/docs' },
},
}),
shipyardDocs({ prerender: false }),
],
})
Express Wrapper
Use Astro’s Node.js adapter in middleware mode and mount it inside Express:
// server.mjs
import express from 'express'
import { handler as astroHandler } from './dist/server/entry.mjs'
const app = express()
// Your auth middleware (e.g. Passport, session)
app.use(session({ /* ... */ }))
app.use(passport.initialize())
app.use(passport.session())
app.use((req, res, next) => {
if (req.isAuthenticated()) return next()
res.redirect('/login')
})
// Mount Astro as the final handler
app.use(astroHandler)
app.listen(3000)
Root Path Redirect
When your docs are the only content, redirect the root path to /docs:
---
// src/pages/index.astro
return Astro.redirect('/docs', 302)
---
This avoids needing a routeBasePath workaround and keeps the URL structure clean.
Key Points
| Setting | Why |
|---|---|
output: 'server' | Enables SSR so middleware runs on every request |
adapter: node({ mode: 'middleware' }) | Lets you mount Astro inside Express |
shipyardDocs({ prerender: false }) | Prevents docs from being pre-rendered at build time, which would bypass auth |
Live Demo
See server mode in action: Server Mode Demo
Deployment Notes
Each adapter has specific deployment requirements. Consult the Astro documentation for your chosen platform: