React + TypeScript app for Punyalink: multi-tenant link-in-bio style stores on subdomains (for example https://yourstore.example.com), backed by the Punyalink API.
- Apex host (
localhost, configured apex domains): marketing landing and store registration (subdomain + email verification flow). - Store host (
{subdomain}.{base domain}or{subdomain}.localhostin dev): public link list, short-link redirects, collections, owner login, and dashboard.
- Node.js 18+ (recommended)
pnpmornpm- Running Punyalink API (see
.env.examplefor default base URL)
-
Clone the repo and install dependencies:
pnpm install
-
Copy environment defaults and adjust:
cp .env.example .env
-
Start the dev server:
pnpm dev
-
Build for production:
pnpm build
Output is written to
dist/. -
Typecheck:
pnpm typecheck
| Variable | Purpose |
|---|---|
VITE_API_URL |
API origin (default http://localhost:3000). |
VITE_BASE_DOMAIN |
Production-style base domain used in URLs and apex parsing (default punyalink.id). |
VITE_APEX_HOSTS |
Comma-separated hostnames treated as apex (no tenant). |
VITE_DEV_TENANT |
On plain localhost / 127.0.0.1, pretend this subdomain so tenant routes load without *.localhost. |
Registration and OTP calls use the API without the x-tenant-subdomain header; tenant-scoped routes send that header automatically.
- Open the apex URL (for example
http://localhost:5173) for the landing page and/register. - To hit a specific store on one origin, either set
VITE_DEV_TENANT=myshopor openhttp://myshop.localhost:5173(with your dev server port).
After signup, the app redirects to {subdomain}.localhost (dev) or {subdomain}.{VITE_BASE_DOMAIN} (non-localhost) at /login.
| Script | Description |
|---|---|
pnpm dev |
Vite dev server |
pnpm build |
Production bundle |
pnpm typecheck |
tsc --noEmit |
- Vite
- React 18
- React Router 7
- Tailwind CSS 4
- UI primitives from Radix / shadcn-style components in
src/app/components/ui/