For developers
Your markup. Your content model. Our hosting.
Every Statically site is a folder of files you write: Blade templates, JSON blueprints, and your own CSS and scripts. There's no theme to fight and no page builder in the way. Every site is fully customisable, from the HTML to the fields your client edits.
acme/
├── site.json menus, settings, forms, SEO
├── blueprints/
│ ├── page.json
│ ├── office.json your fields
│ └── person.json
├── views/
│ ├── layout.blade.php
│ ├── office.blade.php
│ └── components/ your <x-…>
└── assets/
├── tailwind.css or any CSS
└── fonts/
Fully customisable
You decide what the site looks like and what the client can edit.
Any design Write every line of HTML in Blade, with components, partials and layouts. Use Tailwind or your own CSS, self-hosted fonts and Alpine.
Any content model Define offices, people, cases or menus in JSON blueprints, with text, rich text, media, relations, repeaters and more.
A client panel you shape Arrange fields into tabs, sections and columns. Show fields only when they're needed, and decide which settings editors can change and which only admins can.
Any URL structure Top-level pages, patterns like /team/{slug}, pages nested inside pages, or single pages at a fixed address.
Blueprints and templates
Describe the fields. Write the page. That's the whole job.
blueprints/office.json
{
"version": 1,
"handle": "office",
"label": "Offices",
"nestable": true,
"prefix": "offices",
"seo": { "imageFrom": "photo" },
"fields": [
{ "name": "title", "type": "text", "required": true },
{ "type": "tabs", "tabs": [
{ "label": "Details", "fields": [
{ "name": "address", "type": "textarea" },
{ "name": "photo", "type": "media", "accept": ["image"] }
]},
{ "label": "Team", "fields": [
{ "name": "people", "type": "relation", "to": "person", "many": true }
]}
]}
]
}
views/office.blade.php
@extends('site::layout')
@section('content')
<h1>{{ $entry->title }}</h1>
{{ $site->image($entry->get('photo'),
sizes: '100vw', class: 'aspect-video w-full') }}
@foreach ($site->related($entry, 'people') as $person)
<x-person-card :person="$person" />
@endforeach
@endsection
The client gets an Offices form with Details and Team tabs. Visitors get your page as static HTML.
Your workflow
Build locally, check it, push a draft.
Work in your own editor against real content, and keep the site in Git. Or edit the same files in the browser, with a visual builder for blueprints. Both routes save to the same draft, and nothing goes live until you promote it.
Start from a starter
artisan site:create acme --from=brochure-starter
Work on it locally
artisan site:link acme --path=sites/acme
Run every check a save would
artisan site:status acme
Push a draft, then promote it
artisan site:push acme --remote=staging
Built in
The parts you'd otherwise build for every client.
@seo Title, description, canonical, Open Graph and structured data, with per-page overrides for the client.
$site->image() Responsive AVIF and WebP, cropped around the client's chosen focal point.
$site->form() Forms defined in site.json, with a honeypot and email notifications. They work without JavaScript.
menu('main') Menus the client edits, rendered in your own markup with current and active states.
"search": true Site search with Pagefind, built on every publish. It's static files, styled with your CSS.
@analytics Umami, GA4 or Tag Manager, with a consent banner only when one is needed.
The small print
Three things we don't allow, and why.
No Livewire There's no server behind a published site, so Livewire would work in preview and do nothing on the live site. Use Alpine or small scripts instead.
No font CDNs Self-host your fonts in assets/. Loading Google Fonts sends visitors' IP addresses to a third party.
No hand-added tracking Tracking goes through @analytics so the consent banner can't be skipped. The build warns about any script it doesn't recognise.
Build it your way. Let us host it.
Read the full guide, or join the waitlist to get early access.