Quick Start
Quick Start
This guide will walk you through building your first admin interface with Better Admin. In about 15 minutes, you'll have a working admin panel with authentication and CRUD operations.
What you'll build: A complete admin interface for managing users, including login page, protected routes, and a user management interface.
Prerequisites
Before starting, make sure you have:
- Node.js 18 or higher - Download here
- A Next.js 14+ project - Or another React framework
- Basic TypeScript knowledge - We'll use TypeScript for type safety
New to Next.js? Create a new project with npx create-next-app@latest my-admin-app
Installation
Step 1: Install Dependencies
Install all three packages - Better Admin, Better Auth, and Better Query:
npm install better-auth better-query better-adminWhy three packages?
- better-auth handles user authentication
- better-query manages database operations
- better-admin provides UI components to tie everything together
Step 2: Initialize Better Admin
Run the initialization command:
npx better-admin initWhat this does:
- Creates a
better-admin.jsonconfiguration file - Detects your project structure (Next.js app router, pages router, etc.)
- Sets up default paths for components
You'll see output like:
✓ Created better-admin.json
✓ Detected project structure: Next.js App Router
✓ Set component path: src/componentsStep 3: Install shadcn/ui
Better Admin components are built on top of shadcn/ui. If you haven't already, initialize it:
npx shadcn@latest initAlready have shadcn/ui? You can skip this step. Better Admin will work with your existing setup.
Setup Authentication
Now let's set up authentication so users can log in to your admin panel.
Step 1: Create Database Connection
First, create a database connection file:
import Database from "better-sqlite3";
// This creates a SQLite database file
export const db = new Database("app.db");Using a different database? Better Auth supports PostgreSQL, MySQL, and other databases. Check the Better Auth docs for other database options.
Step 2: Setup Better Auth Server
Create your auth configuration on the server side:
import { betterAuth } from "better-auth";
import { db } from "./db";
export const auth = betterAuth({
database: db,
emailAndPassword: {
enabled: true, // Allow login with email and password
},
});What this does:
- Configures Better Auth to use your database
- Enables email/password authentication
- Sets up session management automatically
Step 3: Create Auth Client
Create a client for use in your React components:
import { createAuthClient } from "better-auth/react";
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000",
});
// Export convenient hooks
export const { signIn, signUp, signOut, useSession } = authClient;What this provides:
signIn- Function to sign in userssignUp- Function to register new userssignOut- Function to sign outuseSession- Hook to check if user is logged in
Step 4: Create Auth Provider
Finally, create the auth provider that connects Better Auth to Better Admin:
import { createAuthProvider } from "better-admin";
import { authClient } from "./auth-client";
export const authProvider = createAuthProvider({
authClient,
});Authentication is now set up! Your admin panel can now check if users are logged in and what permissions they have.
Setup Data Provider
Now let's set up the data layer so your admin can manage records in the database.
Step 1: Define Resources
A "resource" is a type of data in your database (like users, posts, products, etc.). Let's create a user resource:
import { betterQuery, createResource } from "better-query";
import { auth } from "./auth";
import { db } from "./db";
// Define what a user looks like
const userResource = createResource({
name: "user",
schema: {
id: { type: "string", primary: true },
name: { type: "string", required: true },
email: { type: "string", required: true },
role: { type: "string" },
},
// Add authentication to all requests
middlewares: [
{
handler: async (context) => {
const session = await auth.api.getSession({
headers: context.request.headers,
});
if (session) context.user = session.user;
},
},
],
// Define who can do what
permissions: {
create: async (context) => !!context.user, // Any logged-in user can create
read: async (context) => !!context.user, // Any logged-in user can read
update: async (context) => {
const user = context.user as any;
return user?.role === "admin"; // Only admins can update
},
delete: async (context) => {
const user = context.user as any;
return user?.role === "admin"; // Only admins can delete
},
},
});
// Create the query client with your resources
export const query = betterQuery({
database: db,
resources: [userResource],
});What are permissions? They control who can create, read, update, or delete records. In this example, any logged-in user can view users, but only admins can modify them.
Step 2: Create Data Provider
Create the data provider that connects your resources to Better Admin:
import { createQueryProvider } from "better-admin";
import { query } from "./query";
export const dataProvider = createQueryProvider({
queryClient: query,
});Data layer is ready! Your admin can now fetch, create, update, and delete users from the database with proper permissions.
Add Admin Components
Now let's add the UI components we'll need for our admin interface.
Step 1: Install Data Table Component
The data table displays lists of records:
npx better-admin add data-tableWhat happens:
✓ Downloading component
✓ Installing dependencies: @tanstack/react-table
✓ Installing shadcn/ui components: table, button, input
✓ Component installed to src/components/admin/data-table.tsxStep 2: Install Form Component
The CRUD form handles creating and editing records:
npx better-admin add crud-formWhat happens:
✓ Downloading component
✓ Installing dependencies: react-hook-form, zod
✓ Installing shadcn/ui components: form, input, button, label
✓ Component installed to src/components/admin/crud-form.tsxWant to see all available components? Run npx better-admin list to browse the complete catalog.
Create Admin Pages
Now let's build the actual admin pages that users will interact with.
Step 1: Login Page
Create a login page so users can authenticate:
"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { authClient } from "@/lib/auth-client";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
export default function LoginPage() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const router = useRouter();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
// Sign in with Better Auth
const result = await authClient.signIn.email({ email, password });
// Redirect to admin if successful
if (!result.error) router.push("/admin");
};
return (
<form onSubmit={handleSubmit} className="max-w-md mx-auto mt-8 space-y-4">
<h1 className="text-2xl font-bold">Admin Login</h1>
<Input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
<Input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
<Button type="submit" className="w-full">Sign In</Button>
</form>
);
}What this does:
- Shows a simple login form
- Calls Better Auth to verify credentials
- Redirects to
/adminon successful login
First time setup: You'll need to create your first admin user in the database before you can log in. See the Auth Provider guide for details.
Step 2: Admin Layout with Auth Protection
Create a layout that protects all admin routes:
"use client";
import { useEffect } from "react";
import { useRouter } from "next/navigation";
import { useBetterAuth } from "better-admin";
import { authClient } from "@/lib/auth-client";
export default function AdminLayout({ children }: { children: React.ReactNode }) {
const { user, isLoading } = useBetterAuth(authClient);
const router = useRouter();
// Redirect to login if not authenticated
useEffect(() => {
if (!isLoading && !user) router.push("/login");
}, [user, isLoading, router]);
// Show loading while checking auth
if (isLoading) return <div>Loading...</div>;
// Don't render until authenticated
if (!user) return null;
return <div className="p-8">{children}</div>;
}What this does:
- Checks if user is logged in using
useBetterAuth - Redirects to
/loginif not authenticated - Shows a loading state while checking
- Only renders children if user is authenticated
Why a layout? In Next.js App Router, layouts wrap all pages in a folder. This means all pages under /admin are automatically protected.
Step 3: Users List Page
Create a page to view and manage users:
"use client";
import { useQuery } from "better-admin";
import { query } from "@/lib/query";
import { DataTable } from "@/components/ui/data-table";
import { Button } from "@/components/ui/button";
import Link from "next/link";
export default function UsersPage() {
// Fetch users from the database
const { list } = useQuery("user", query);
const { data, isLoading } = list.useQuery();
// Define which columns to show
const columns = [
{ accessorKey: "name", header: "Name" },
{ accessorKey: "email", header: "Email" },
];
if (isLoading) return <div>Loading...</div>;
return (
<div>
<div className="flex justify-between mb-4">
<h1 className="text-2xl font-bold">Users</h1>
<Button asChild>
<Link href="/admin/users/create">Create User</Link>
</Button>
</div>
<DataTable columns={columns} data={data || []} />
</div>
);
}What this does:
- Uses
useQueryto fetch users from the database - Displays users in a sortable, filterable data table
- Provides a button to create new users
Data updates automatically: When you create or update a user, the table refetches automatically thanks to React Query.
Step 4: Create User Page
Create a page to add new users:
"use client";
import { useQuery } from "better-admin";
import { query } from "@/lib/query";
import { CrudForm } from "@/components/ui/crud-form";
import { useRouter } from "next/navigation";
export default function CreateUserPage() {
const { create } = useQuery("user", query);
const router = useRouter();
// Define the form fields
const fields = [
{ name: "name", label: "Name", type: "text", required: true },
{ name: "email", label: "Email", type: "email", required: true },
];
return (
<div>
<h1 className="text-2xl font-bold mb-6">Create User</h1>
<CrudForm
fields={fields}
onSubmit={async (data) => {
// Save to database
await create.mutateAsync(data);
// Go back to users list
router.push("/admin/users");
}}
submitLabel="Create"
/>
</div>
);
}What this does:
- Shows a form with name and email fields
- Validates input automatically
- Saves to the database when submitted
- Redirects back to the users list
You now have a complete admin interface! You can view users, create new ones, and everything is protected by authentication.
Environment Variables
Create a .env.local file in your project root with these variables:
# Your app's URL (use your production URL in production)
NEXT_PUBLIC_APP_URL=http://localhost:3000
# Secret key for Better Auth (generate a random string)
BETTER_AUTH_SECRET=your-secret-key-here-change-this
# Database connection (SQLite in this example)
DATABASE_URL=sqlite:app.dbSecurity: Never commit the .env.local file to version control. Generate a strong random string for BETTER_AUTH_SECRET in production.
How to generate a secret key:
# On macOS/Linux
openssl rand -base64 32
# Or use an online generator
# https://generate-secret.vercel.app/Run Your App
Start the development server:
npm run devYour admin is now running! Open your browser and visit:
- http://localhost:3000/login - Login page
- http://localhost:3000/admin/users - Users list (after login)
First login: You'll need to create your first admin user directly in the database. See the troubleshooting section below for how to do this.
What You've Built
Congratulations! You now have:
✅ A complete authentication system with Better Auth
✅ A protected admin area that requires login
✅ A users list page with a data table
✅ A create user form
✅ Automatic data fetching and caching
✅ Type-safe operations from database to UI
Next Steps
Now that you have the basics working, you can:
Add More Features
-
Add Edit Functionality
- Install the edit button component
- Create an edit page for users
- Learn more in Components
-
Add Delete Functionality
- Install the delete button component
- Add confirmation dialogs
- Handle optimistic updates
-
Add More Resources
- Create resources for posts, products, orders, etc.
- Define relationships between resources
- Learn more in Data Provider
Enhance Your Admin
-
Improve the UI
- Add a sidebar navigation
- Create a dashboard
- Add charts and statistics
-
Add Advanced Features
- Implement filtering and search
- Add bulk operations
- Set up file uploads
-
Customize Components
- Modify installed components to match your brand
- Create custom input types
- Build custom views
Learn More
- Dashboard Guide - Build custom dashboards with stats and charts
- Auth Provider Guide - Master authentication and permissions
- Data Provider Guide - Advanced data operations and relationships
- Components - Browse all available components
- Examples - See complete example applications
Common Issues
"Cannot find module 'better-auth'"
Problem: The package isn't installed.
Solution: Install it with:
npm install better-auth"Database not found"
Problem: The database file path is incorrect or the database hasn't been created.
Solution:
- Check that
DATABASE_URLin.env.localmatches yourlib/db.tspath - The database file will be created automatically when you first run the app
- Make sure the directory exists and is writable
Components not found
Problem: You're trying to use a component that hasn't been installed.
Solution: Install the component with:
npx better-admin add <component-name>For example:
npx better-admin add data-tableTypeScript errors
Problem: TypeScript version is too old.
Solution: Update TypeScript:
npm install -D typescript@latest"No users found" / Can't log in
Problem: You need to create your first admin user.
Solution: Create a user directly in the database:
Option 1: Using SQLite CLI
sqlite3 app.dbINSERT INTO user (id, email, name, role)
VALUES ('1', 'admin@example.com', 'Admin User', 'admin');Option 2: Using a seed script
Create scripts/seed.ts:
import { db } from '../lib/db';
db.exec(`
INSERT INTO user (id, email, name, role)
VALUES ('1', 'admin@example.com', 'Admin User', 'admin');
`);
console.log('✓ Admin user created');Then run:
npx tsx scripts/seed.tsProduction setup: In production, use a proper user signup flow or an admin creation script. Never hardcode credentials!
Support
Need help? Here are your options:
- GitHub Issues - Report bugs or request features
- Documentation - Browse the complete documentation
- Examples - Learn from complete examples
- Community - Join discussions with other developers
Congratulations! You've successfully built your first admin interface with Better Admin. Keep exploring the documentation to learn about advanced features.