The Batteries-Included
Full-Stack Framework
for Go & React

Opinionated by design. Describe a resource and Grit writes the Go model, API, migrations, TypeScript types, React hooks and admin screen. Auth, RBAC, jobs, storage, realtime, observability and deploy: all configured out of the box.

Terminal
$iwr -useb https://gritframework.dev/install.ps1 | iex

Detects an existing install and runs grit update, otherwise pulls the right binary for your OS. All install options

Built on
Go
Postgres
Redis
Docker
Next.js
internal/handlers/product.go
package handlers
// Written for you, along with the model, migration,
// service, routes, Zod schema, TS types, React Query
// hooks and a working admin page.
func (h *ProductHandler) List(c *gin.Context) {
var products []models.Product
h.DB.
Where("user_id = ?", c.GetString("user_id")).
Find(&products)
c.JSON(http.StatusOK, gin.H{
"data": products,
})
}
grit generate resource Product: model, API, types, hooks and admin screen
Benchmarks

The same CRUD API, built in every framework

One Postgres, identical container limits, the same 10,000 rows, the same k6 script, every framework in production shape and on its own ORM. Each has a page showing exactly how to reproduce it, including the two rows against Bun that repeated measurement could not separate, and the three bugs this benchmark found in Grit itself.

Requests per second, GET /products/:id

One indexed lookup and a JSON encode: the cleanest read of framework overhead

Gritvseach framework in its own colour

Each framework was run head to head against Grit, back to back, three times, medians reported, zero failed requests. 50 concurrent users, 4 CPUs and 2 GB per container, one shared Postgres, the same 10,000 rows, every framework on its own ORM. Bar heights are scaled within each pair, because each pair is a separate run. Compare the two bars inside a group, not heights across groups. The printed figures are the real measurements.

Methodology

Speed is the easy half

The benchmark above measures a request. This measures a Monday: how many of the 26 things a real product needs are already running the first time you open the app. Grit has 22. The 4 it does not have are in the table too, because a comparison where one column wins every row is not a comparison.

GeneratedBuilt inOfficial add-onThird partyNone

Identity

Security and compliance

Operations

Developer experience

Reach

Running without installing anything: 22/26 in Grit. The next closest is 10/26.

Every row here can be built in every framework listed. The table is not about what is possible, it is about how much is already running the first time you open the app. Django's admin and Laravel's queues and observability are first rate, and they are marked as such. Express is a router and Bun is a runtime, so Express scoring low is a statement about scope and not about quality, which is also why Bun is not a column here at all.

What ships in the box
60-second tour

What is Grit?

A full-stack meta-framework that turns one CLI command into a working Go API, React frontend, and admin panel, with everything you usually wire up by hand already wired up.

STEP / 01

Scaffold a real project

Pick your architecture (single binary, monorepo, mobile, desktop) and one command scaffolds the entire repo: Go API, React frontend, admin panel, Docker, CI, all wired together.

$grit new my-app --triple
STEP / 02

Generate full-stack features

Describe a resource (Product · Order · Invoice). Grit emits the Go model, service, handler, routes, Zod schema, React hooks, and admin page: typed end-to-end, no glue code.

$grit generate resource Product
STEP / 03

Ship to a real server

grit deploy cross-compiles, uploads, sets up systemd, and configures Caddy with auto-TLS. Or push to git and let your platform of choice run the bundled Dockerfile.

$grit deploy --domain acme.app

What you get out of the box

  • No glue code between backend and frontend: generated together, always in sync
  • Production batteries: auth + 2FA, OAuth, storage, jobs, AI, observability
  • Secure-by-default headers; OWASP Top 10:2025 hardened out of the box
Read quick start
Every platform

One framework.
Every platform.

Web, desktop, mobile and a documented API, from one Go backend and one set of generated types. Every screenshot below is a real generated project, next to the commands that produce it.

localhost:3001/dashboard
Web: A Next.js app and a real admin panel

A screenshot of a generated project, not a mockup.

A Next.js app and a real admin panel

Two front-ends against one Go API: a public web app and a resource-driven admin panel with tables, filters, multi-step forms and RBAC already wired up.

  1. 1
    grit new myapp --tripleGo API + web app + admin panel
  2. 2
    cd myapp && docker compose up -dPostgres, Redis, MinIO, Mailhog
  3. 3
    pnpm install && grit migrate && grit seeddeps, tables, a demo admin login
  4. 4
    grit startall three, one terminal

Admin on :3001, web on :3000, API on :8080.

Code generation

One command.
The whole form.

Not a scaffold you finish by hand. Uploads that know what they accept, foreign keys you can fill without leaving the page, line-item tables that total themselves, wizards that save a step at a time, and a desktop app that keeps taking input with the network off.

localhost:3001
File uploads: Four kinds of upload, four words

Four kinds of upload, four words

Declare a field as file or files and add what it accepts: image, pdf, zip, doc, video, or a bracketed list. You get a dropzone with the right filter, image previews, per-type icons, size caps and progress, on both create and edit.

What produced it
grit generate resource Product --fields \
  "name:string,category:belongs_to:Category,price:float,\
   cover:file:image,gallery:files:image,\
   spec_sheet:file:pdf,downloads:files:[zip,doc],\
   description:richtext,published:bool"

One command: model, migration, API, types, hooks and this form.

What to notice

Uploads go browser-to-storage through a presigned URL, and the field’s accept list is enforced on the server too: a field declared file:pdf will not take a PNG even if the client asks nicely.

Themes

Four themes.
None of them look generated.

Every scaffolded admin ships with four complete themes: not palettes, but coordinated typography, auth pages, dashboard and brand colour. Pick one at grit new, or change your mind later with one line in .env.

localhost:3001/dashboard
The Grit admin panel in the Atlas theme

Atlas

The default. Professional blue on white: teams, dashboards, internal tools.

At scaffold time
grit new myapp --theme atlas
Or any time after
THEME=atlas

One line in .env. Themes are CSS variables, so switching one costs a restart, not a rewrite.

Real screenshots of a generated admin: same resources, same seeded data, same viewport. Only the theme changes.

Batteries included

The parts you always end up
building anyway.

Permissions, brute-force protection, golden-signal metrics, an audit timeline, scheduled backups, S3 uploads, a job queue and a cron scheduler: each with a real screen, not a config file and a README. This is what grit new gives you on day one.

localhost:3001/system/roles
Roles & permissions: Permissions you can actually reason about

Permissions you can actually reason about

Every resource you generate registers its own create / view / edit / delete permissions automatically. Roles are rows in your database, not constants in a file, so the people running the app can change them without a deploy.

What to notice

Grant a whole resource and it keeps any action added to it later, so generating a new resource never silently widens or narrows an existing role.

Every one of these ships with grit new. There is nothing to install and nothing to wire up.

Authentication

Auth you would otherwise
spend a month on.

Sign-in pages, a JWT pair, revocable server-side sessions, Google and GitHub, TOTP with backup codes, database-backed roles, and enterprise SSO over OIDC or SAML. Working on the first run, not a tutorial to follow.

localhost:3001/login
Sign in: The pages you would have built on day one

The pages you would have built on day one

Login, register, forgot-password and reset-password, themed with the rest of the admin and wired to a Go API that already hashes with bcrypt and issues a JWT pair.

  • Access token (15m) + refresh token (168h), both configurable
  • HttpOnly cookies for the browser, Bearer headers for mobile and desktop
  • CSRF enforced on cookie-authenticated mutations, transparent to Bearer clients
  • Login rate-limited to 5 attempts / 15 min in production; register to 3

Not included, so you are not surprised later: there is no email-verification flow (the field exists and social sign-in sets it, but nothing sends a verification mail), and no API-key authentication; machine callers use the same JWT flow.

Compliance

The questions enterprise
buyers ask first.

GDPR export and erasure with a tamper-evident journal, SSO per customer over OIDC or SAML, access-review campaigns for SOC 2, and a hash-chained audit trail that exports to your SIEM. Built in, not bought later.

localhost:3001/system/gdpr
GDPR: Article 15 and Article 17, as buttons

Article 15 and Article 17, as buttons

Right-to-access exports and right-to-erasure, with every erasure written to a hash-chained journal that can prove it has not been edited since.

  • Export returns one JSON file: profile, uploads, sessions, activity, 2FA state
  • Erase hard-deletes personal records across nine tables and anonymises the account
  • The user id survives as a tombstone, so the audit trail stays readable
  • The journal stores no PII: just ids, counts, actor and a SHA-256 chain
  • A verify pass replays the chain and names the row where it breaks

What Grit does not ship: a cookie-consent banner, a signed SBOM, or data-residency controls. Field-level encryption, a go-live checklist and dependency scanning in CI are included, but the paperwork side of a certification is still yours.

Customisation

One file describes
the whole screen.

The generator writes a resource definition; after that it is ordinary TypeScript you own. Columns, filters, row and bulk actions, twenty field types, wizards, dropzone variants, changed by editing a file, not by fighting a generator that wants to overwrite it.

apps/admin/resources/products.tsgenerated, then yours
import { defineResource } from "@/lib/resource";
export const productResource = defineResource({
name: "Product",
slug: "products",
endpoint: "/api/products",
icon: "Package",
label: { singular: "Product", plural: "Products" },
// sheet · modal · page · modal-steps · page-steps
formView: "sheet",
// Sidebar placement
group: "Catalog", // section heading to sit under
adminOnly: false, // hide from non-admins
hidden: false, // keep routable, drop from the sidebar
table: { /* columns, filters, actions, export, import */ },
form: { /* fields, layout, steps, groups */ },
// Four auto stat cards, or your own
stats: true,
dashboard: { enabled: true },
});
Form views (5)
sheetmodalpagemodal-stepspage-steps
Table actions (5)
createvieweditdeleteexport

grit generate resource writes this file for you. Everything past that point is ordinary TypeScript you own. There is no regeneration step that overwrites your edits.

Infrastructure

Your database.
Your bucket. Your Redis.

Grit is opinionated about structure, not about who you rent from. Each of these is one environment variable, with a local default that works before you have any cloud account at all.

Postgres in production, SQLite when you just want to run it

One DATABASE_URL. The driver is chosen by the shape of the DSN, so moving from a local file to a managed Postgres is an env change and nothing else. GORM is the ORM either way, so your models and migrations do not care.

Switch withDATABASE_URLsqlite:… or a postgres:// URL

PostgreSQL

default

The default, and what docker compose brings up locally.

DATABASE_URL=postgres://user:pass@host:5432/db?sslmode=require

Any Postgres-compatible host works: it is a standard DSN, not a per-vendor integration.

SQLite

A file, or :memory: for tests. Pure-Go driver, so no CGO.

DATABASE_URL=sqlite:./app.db
DATABASE_URL=sqlite::memory:

Ideal for the first five minutes and for the generated Go test suite.

What Grit does with it
  • Connection pool tuned on startup: 100 open, 10 idle, 30-minute max lifetime
  • AutoMigrate wired to every generated model, reporting what changed
  • GORM Studio at /studio to browse and edit rows
  • Sessions, roles, permissions and the audit log are all ordinary tables

MySQL is not supported. The connector picks SQLite or Postgres by DSN prefix: there is no third dialector to fall back to.

internal/models/product.go
// One URL. The driver is chosen by the DSN's shape.
// DATABASE_URL=sqlite:./app.db
// DATABASE_URL=sqlite::memory:
// DATABASE_URL=postgres://user:pass@host:5432/db?sslmode=require
// Models are ordinary GORM structs — generated, then yours.
type Product struct {
ID string `gorm:"primaryKey" json:"id"`
Name string `gorm:"not null" json:"name"`
CategoryID string `json:"category_id"`
Category *Category `json:"category,omitempty"`
Price float64 `json:"price"`
CreatedAt time.Time `json:"created_at"`
}
// grit migrate runs AutoMigrate across every registered model,
// and reports what it created or altered rather than doing it silently:
// + created *models.Product
// ~ altered *models.Invoice (+2 columns)
Deployment

It is a Go binary
and some containers.

Which means it runs anywhere: a $5 VPS, a managed platform, or your own Docker host. Pick a target for the actual steps, what it costs, and the thing that catches people out.

Runs the Docker image close to your users, with a real disk if you need one.

Good fit: A single Go binary you want in several regions without running servers. The best fit for Grit’s single-binary mode.

  1. 1

    Launch without deploying

    Let Fly detect the Dockerfile and write a fly.toml, but stop before it ships anything: the defaults need two changes first.

    fly launch --no-deploy
  2. 2

    Point the health check at the API

    Fly checks `/` by default. Grit serves its health endpoint at `/api/health`, so without this the machine is marked unhealthy and cycled forever while the app is running perfectly.

    [http_service]
      internal_port = 8080
      force_https = true
      auto_stop_machines = "suspend"
      auto_start_machines = true
      min_machines_running = 1
    
      [[http_service.checks]]
        interval = "15s"
        timeout = "3s"
        grace_period = "10s"
        method = "GET"
        path = "/api/health"
  3. 3

    Attach Postgres and Redis

    Attaching sets DATABASE_URL for you. Redis comes from Upstash through Fly and gives you REDIS_URL.

    fly postgres create --name my-app-db
    fly postgres attach my-app-db
    
    fly redis create
From~$5Ops effortLow
Managed Postgres
Managed Redis
Persistent disk
What catches people out

Machines suspend on idle by default. The first request after a quiet period pays the wake-up cost: set `min_machines_running = 1` for anything user-facing.

Not for

Teams that want a click-through dashboard for everything: Fly is CLI-first and expects you to read a TOML file.

One backend

Write the API once.
Ship it to every client.

The generator emits the Go handler and the typed client: schemas, TypeScript types and React Query hooks. The same hook then works in a Next.js app, a TanStack SPA, an Expo phone app and an offline-capable desktop binary. Rename a field in the Go struct and every one of them stops compiling until you fix it.

internal/handlers/product.gowritten once
package handlers
// Written once by:
// grit generate resource Product
func (h *ProductHandler) List(c *gin.Context) {
var products []models.Product
h.DB.
Where("user_id = ?", c.GetString("user_id")).
Find(&products)
c.JSON(http.StatusOK, gin.H{
"data": products,
})
}
apps/web/app/products/page.tsx
'use client'
import { useProducts } from '@/hooks/use-products'
export default function ProductsPage() {
const { data: products, isLoading } = useProducts()
if (isLoading) return <Skeleton />
return (
<ul>
{products.map((p) => (
// p is typed from the Go struct — rename the
// field in Go and this stops compiling.
<li key={p.id}>{p.name}{p.price}</li>
))}
</ul>
)
}

Generated React Query hook, generated types. No fetch wrapper to write.

The Grit Core

One CLI: eight production
primitives wired together

Grit is the chip on the board. Auth, jobs, storage, AI, observability, webhooks, realtime, and cache all light up the moment you scaffold, so you spend your time on product not plumbing.

GRIT
v3.170.0 · production-ready
GRIT-FW-A1
Auth

JWT · OAuth · 2FA

AI Gateway

100+ models · stream

File Storage

S3 · R2 · MinIO

Background Jobs

asynq · retries

Webhooks

Stripe · HMAC · replay

Realtime Hub

WebSockets · channels

Redis Cache

middleware · TTL

Transactional Mail

Resend · templates

One Project · Every Primitive

Ship anything. Wire everything.

From git push to a running app with auth, storage, jobs, realtime, and AI all on the same wire: no glue code, no dashboard tabs to hunt through.

Git push deploys
Docker images
Resource generator
Compose stacks
Grit
Grit Framework
Your Project
LIVEv3.23
Auth + RBAC
File storage
Domains + SSL
Realtime hub
AI Gateway
Pulse observability
CLI deploy

A framework for developers and agents

Grit has opinions on everything: routing, queues, auth, storage, AI. That is thousands of decisions an AI agent does not have to make, and the code it writes lands in the same shape a person would have written.

Generates Go + React from one CLI commandShips a SKILL.md so agents know the patternsAI Gateway: 100+ models via one API keyOWASP 2025 hardened: secure by default
func (h *AuthHandler) Login(c *gin.Context) {
var req loginRequest
c.ShouldBindJSON(&req)
var user models.User
h.DB.Where("email = ?", req.Email).
First(&user)
if !user.CheckPassword(req.Password) {
c.JSON(401, gin.H{
"error": "Invalid credentials",
})
return
}
tokens, _ := h.AuthService.
GenerateTokenPair(user.ID, user.Email, user.Role)
c.JSON(200, gin.H{
"data": gin.H{"user": user, "tokens": tokens},
})
}

Monitor and fix issues with Pulse

Pulse gives full observability: find errors and performance issues before your team does. Mounted at /pulse/ui on every Grit project.

  • Fix errors and performance with recommended solutions
  • Trace requests, jobs, DB queries, cache hits, errors
  • Wire k6 test runs into the live latency timeline
Explore Pulse
Requests
+14% vs yesterday
124.2Krequests
2xx 122.5K
4xx 1,151
5xx 324
02 Nov 18:00 UTC03 Nov 18:00 UTC
Duration
125ms – 2.2s

The best partner to any front-end

Grit scaffolds Next.js and TanStack Router front-ends, and generates the typed client for both. The API itself is plain REST with an OpenAPI document, so anything that speaks HTTP can call it, but these are the ones we generate and test.

Explore front-ends
users.expo.tsx
users.tsx
users.desktop.tsx
users.next.tsx

Flexible Architecture

Choose how you build

Coming from Laravel? Choose Single. MERN stack? Choose Double. Building a SaaS? Choose Triple.

Single

Go + embedded SPA

--single

Double

Web + API monorepo

--double

Triple

Web + Admin + API

--triple

API Only

Go backend only

--api

Mobile

API + Expo

--mobile

One-Command Deploy

From code to production
in one command

grit deploy builds your app, uploads via SSH, configures systemd, and sets up Caddy with auto-TLS.

  • 01Cross-compiles Go binary for Linux (CGO_ENABLED=0)
  • 02Builds frontend if present (pnpm build)
  • 03Uploads binary via SCP
  • 04Creates systemd service with auto-restart
  • 05Configures Caddy reverse proxy with Let's Encrypt TLS
Deploy guide
Terminal
$ grit deploy --host deploy@server.com --domain myapp.com
→ Building frontend...
→ Building Go binary (linux/amd64)...
→ Uploading binary to /opt/myapp/
→ Setting up systemd service...
→ Configuring Caddy reverse proxy...
✓ Deployment successful!
Live at: https://myapp.com

Framework Comparison

How Grit compares

FeatureGritNext.jsLaravel
Go Backend
React Frontend
Admin Panel
Code Generator
JWT + OAuth2
Two-Factor Auth
File Storage
Background Jobs
AI Integration
One-Command Deploy
Multiple Architectures
Desktop App
Offline-First Sync
Audit Log + Hash Chain
Feature Flags
OWASP 2025 Hardened

Built something with Grit?

There are no testimonials here yet, and there will not be any invented ones. If Grit is running something of yours in production, tell us about it (the good and the parts that hurt) and it goes on this page with your name and a link back to you.

Muke JohnBaptist
“I built Grit because I was tired of spending weeks setting up the same boilerplate for every project. Auth, admin panels, file uploads, background jobs. They should just work. Now they do. One command, and you have a production-ready app. That's the framework I wanted to use.”
Muke JohnBaptist
Creator of Grit Framework

Sponsors

Backed by developers like you

Grit is free and MIT licensed. Sponsors fund the features, docs and releases, and get their name in front of everyone who builds with it.

This spot is open

Grit has no sponsors yet. Be the first, your logo goes at the top of this page, on the home page, in the README, and inside the CLI.

Become the founding sponsor

FAQ

Frequently asked questions

Do I need to know Go to use Grit?

Basic Go knowledge helps, but Grit generates most of the code for you. The generated code follows clear patterns (handler → service → model) that are easy to extend. If you know any backend language, you'll pick it up fast.

Can I use Grit with an existing project?

Grit is designed for greenfield projects. It scaffolds the full project structure. However, you can use grit generate resource in existing Grit projects to add new features incrementally.

Is Grit production-ready?

Yes. Every scaffolded project includes JWT auth, RBAC, rate limiting (Sentinel), observability (Pulse), error handling, CORS, gzip compression, connection pooling, and graceful shutdown. It's designed for production from day one.

What's the difference between Single and Triple architecture?

Single embeds the React SPA into the Go binary via go:embed, leaving one file to deploy. Triple is a Turborepo monorepo with separate web app, admin panel, and API, and is the better fit for teams and complex products.

Can I switch from Next.js to TanStack Router later?

The backend (Go API) is identical regardless of frontend choice. You'd need to rebuild the frontend pages, but all hooks, types, and API patterns are the same. The admin panel components are also framework-agnostic React.

How does grit deploy work? Is it like Vercel?

grit deploy is for self-hosted deployments. It SSHs to your server, uploads the binary, configures systemd, and sets up Caddy with auto-TLS. For Vercel/Railway, just push to git and the Dockerfile is included.

Is Grit open source?

Yes, Grit is fully open source under the MIT license. The CLI, all plugins, and the documentation are on GitHub.

Community

Build alongside other Grit developers

Join the WhatsApp community for questions, tutorials, and guidance from people shipping Grit apps, and from the person who builds it. No question is too small.

Join the community

Start using Grit today

Install the CLI and scaffold your first project. Or dive into the docs to plan your architecture first.

go install github.com/MUKE-coder/grit/v3/cmd/grit@latest
grit new my-app