SaaS starter
Marketing pages, sign-in and sign-up screens, and an authenticated app area with a mocked session you replace with real auth.
A multi-page single-page app built on a tiny hash router (no extra dependency): landing, pricing, sign in, sign up, an app dashboard and a settings page. Authentication is deliberately mocked in src/auth.tsx and labelled as such in the UI; the session lives in localStorage so you can click through every flow before wiring up a real provider.
- react
- saas
- auth
- multi-page
- tailwind
- typescript
This preview is the template's actual file tree and source code — exactly what is copied into a new project. Codexa does not publish screenshots or mock-ups of templates; run the project to see it rendered.
Files18 files
import { useState, type FormEvent } from "react";
import { useAuth } from "../auth";
import { Link, navigate } from "../router";
function MockNotice() {
return (
<p className="rounded-md border border-amber-300 bg-amber-50 p-3 text-xs text-amber-900">
<strong>Mock authentication.</strong> Any email is accepted and nothing is verified. Replace <code>src/auth.tsx</code> with real auth before shipping.
</p>
);
}
export function SignIn({ redirectTo }: { redirectTo: string }) {
const { signIn } = useAuth();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState<string | null>(null);
function submit(e: FormEvent) {
e.preventDefault();
if (!email.includes("@")) return setError("Enter a valid email address.");
if (password.length < 8) return setError("Password must be at least 8 characters.");
signIn(email);
navigate(redirectTo);
}
return (
<form onSubmit={submit} className="mx-auto max-w-sm space-y-4 rounded-xl border border-slate-200 bg-white p-6">
<h1 className="text-xl font-semibold">Sign in</h1>
<MockNotice />
<label className="block text-sm">
<span className="font-medium">Email</span>
<input value={email} onChange={(e) => setEmail(e.target.value)} type="email" required className="mt-1 w-full rounded-md border border-slate-300 px-3 py-2" />
</label>
<label className="block text-sm">
<span className="font-medium">Password</span>
<input value={password} onChange={(e) => setPassword(e.target.value)} type="password" required className="mt-1 w-full rounded-md border border-slate-300 px-3 py-2" />
</label>
{error ? (
<p role="alert" className="text-sm text-red-600">
{error}
</p>
) : null}
<button type="submit" className="w-full rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700">
Sign in
</button>
<p className="text-center text-sm text-slate-600">
No account? <Link to="/signup" className="text-blue-600">Sign up</Link>
</p>
</form>
);
}
export function SignUp() {
const { signUp } = useAuth();
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [error, setError] = useState<string | null>(null);
function submit(e: FormEvent) {
e.preventDefault();
if (name.trim().length < 2) return setError("Enter your name.");
if (!email.includes("@")) return setError("Enter a valid email address.");
signUp(name.trim(), email);
navigate("/app");
}
return (
<form onSubmit={submit} className="mx-auto max-w-sm space-y-4 rounded-xl border border-slate-200 bg-white p-6">
<h1 className="text-xl font-semibold">Create your account</h1>
<MockNotice />
<label className="block text-sm">
<span className="font-medium">Name</span>
<input value={name} onChange={(e) => setName(e.target.value)} required className="mt-1 w-full rounded-md border border-slate-300 px-3 py-2" />
</label>
<label className="block text-sm">
<span className="font-medium">Email</span>
<input value={email} onChange={(e) => setEmail(e.target.value)} type="email" required className="mt-1 w-full rounded-md border border-slate-300 px-3 py-2" />
</label>
{error ? (
<p role="alert" className="text-sm text-red-600">
{error}
</p>
) : null}
<button type="submit" className="w-full rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700">
Create account
</button>
<p className="text-center text-sm text-slate-600">
Already registered? <Link to="/signin" className="text-blue-600">Sign in</Link>
</p>
</form>
);
}
Dependencies
Documented from the template's package.json. The runtime has a prebuilt dependency set for Vite + React + TypeScript, so installs are fast.
| Package | Version |
|---|---|
| react | ^19.1.0 |
| react-dom | ^19.1.0 |
| vite | ^6.3.0 |
| @vitejs/plugin-react | ^4.4.0 |
| tailwindcss | ^4.1.0 |
| @tailwindcss/vite | ^4.1.0 |
| typescript | ^5.8.0 |
How it runs
- Framework
- Vite + React + TypeScript
- Dev command
- npm run dev
- Build command
- npm run build
- Output directory
- dist
- Port
- 5173
- Opens at
- /
- Difficulty
- intermediate
Projects run in an isolated sandbox with a live preview. Read the projects documentation for details on settings, snapshots and deployment.
Need the raw data? GET /api/v1/templates/vite-react-saas returns this template with its files.
Build on SaaS starter
Create a free account, start from this template, and ask the AI agent to turn it into your app.