TailwindSQL
Write SQL queries inline with Tailwind-like utility syntax.
Reusable, composable.
Why TailwindSQL?
Traditional SQL in React requires separating data fetching from rendering. TailwindSQL lets you declare queries inline within your components, making data dependencies explicit and components self-contained.
TailwindSQL
// TailwindSQL approach
import { QueryBlock } from "../tailwindsql.config";
function UsersPage() {
return (
<QueryBlock query="select-all from-[User]">
{(users) => (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)}
</QueryBlock>
);
}Setup
Configure TailwindSQL once with your database adapter. The adapter is any function that takes a SQL string and returns results.
1. Create adapter
// app/actions.ts
"use server";
import { prisma } from "./db";
export async function executeSQL(sql: string) {
return prisma.$queryRawUnsafe(sql);
}2. Configure TailwindSQL
// tailwindsql.config.ts
import { configTailwindSQL } from "@repo/tailwindsql";
import { executeSQL } from "./app/actions";
export const { QueryBlock } = configTailwindSQL({
adapter: executeSQL,
});Live Examples
Example 1: All Users — Default JSON output
Code
// AllUsers.tsx — Default JSON output
import { QueryBlock } from "../tailwindsql.config";
export function AllUsers() {
return <QueryBlock query="select-all from-[User]" />;
}Output
Loading...
Example 2: User Names — Custom render function
Code
// UserList.tsx — Custom render
import { QueryBlock } from "../tailwindsql.config";
export function UserList() {
return (
<QueryBlock query="select-[id,name,email] from-[User]">
{(users) => (
<ul className="space-y-2">
{users.map((user) => (
<li key={user.id} className="p-3 rounded-lg bg-neutral-100">
<span className="font-medium">{user.name}</span>
<span className="text-sm opacity-60 ml-2">{user.email}</span>
</li>
))}
</ul>
)}
</QueryBlock>
);
}Output
Loading...
Syntax Reference
| TailwindSQL | Generated SQL |
|---|---|
| select-all | SELECT * |
| select-[id,name] | SELECT id, name |
| from-[users] | FROM users |
| where-[age>18] | WHERE age>18 |
| orderby-[created_at] | ORDER BY created_at |
| limit-[10] | LIMIT 10 |
| offset-[5] | OFFSET 5 |
| join-[posts] | JOIN posts |
| groupby-[status] | GROUP BY status |