- TypeScript 95.8%
- CSS 4%
- Dockerfile 0.1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| data | ||
| prisma | ||
| public | ||
| scripts | ||
| src | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yml | ||
| Dockerfile | ||
| eslint.config.mjs | ||
| next.config.ts | ||
| package-lock.json | ||
| package.json | ||
| postcss.config.mjs | ||
| prisma.config.ts | ||
| README.md | ||
| tailwind.config.ts | ||
| tsconfig.json | ||
Partner Carrd
A self-hosted portfolio, link-in-bio, and lightweight storefront builder.
Partner Carrd is a full-stack website builder for creators who want a polished online presence without depending on a hosted site-building platform. It combines a block-based visual editor with a fast public site, first-party analytics, managed media, version history, backups, and optional Stripe Checkout.
The project is built as a single-site, single-admin application. It is a strong fit for a personal portfolio, creator hub, campaign page, or small merchandise storefront that you want to own and operate yourself.
This is an independent project inspired by the simplicity of one-page website builders. It is not affiliated with Carrd.
What it does
From one private admin workspace, you can compose and publish a responsive website without editing source code:
- Build pages from hero, links, text, gallery, portfolio, contact, merchandise, and custom sections
- Reorder sections with drag and drop and start quickly from section templates
- Control colors, typography, spacing, cards, navigation, backgrounds, and intro effects
- Upload images through a managed pipeline that creates WebP display, preview, thumbnail, and hero variants
- Create dedicated portfolio case-study pages at
/portfolio/[slug] - Schedule links, target them by device or country, and configure A/B variants
- Manage SEO metadata, social previews, the favicon, and footer content
- Track privacy-minded first-party analytics without a third-party analytics account
- Restore prior content versions and create, download, or restore SQLite backups
- Sell merchandise through optional hosted Stripe Checkout and manage order fulfillment
Project scope
Partner Carrd deliberately favors a focused, maintainable architecture over becoming a general-purpose website platform.
| In scope | Out of scope |
|---|---|
| One public website and one admin workspace | Multi-tenant accounts or customer workspaces |
| Creator portfolios, link hubs, and small storefronts | A general-purpose CMS or page builder |
| Self-hosting on a single persistent instance | Serverless deployments with ephemeral storage |
| Local SQLite data, uploads, history, and backups | Horizontally scaled database and object storage |
| Optional Stripe-hosted checkout | Inventory, shipping, tax, or marketplace management |
| First-party traffic and conversion analytics | Enterprise analytics or behavioral replay |
This scope keeps the application straightforward to deploy, inspect, customize, and back up. For higher traffic or multiple application replicas, SQLite and filesystem storage should be replaced with shared production services.
Technical highlights
- Next.js App Router powers the public site, admin workspace, and API routes in one application.
- Typed content model and Zod validation protect saved and imported site data.
- Prisma + SQLite provide a portable data layer with committed migrations.
- Server-side image processing uses Sharp to validate uploads, strip metadata, and generate optimized variants.
- Content recovery includes the newest 30 editor snapshots plus independent full-database backups.
- Security controls include hashed passwords and sessions, login throttling, same-origin checks, upload validation, path and URL sanitization, restrictive headers, and session revocation after password changes.
- First-party analytics stores page views, outbound clicks, referral hosts, approximate unique visitors, location, device, browser, section activity, and store conversions locally.
Technology
| Layer | Technology |
|---|---|
| Framework | Next.js 16, React 19 |
| Language | TypeScript |
| Styling | Tailwind CSS 4 |
| Database | SQLite, Prisma ORM 6 |
| Validation | Zod |
| Authentication | Cookie sessions, bcrypt |
| Media | Sharp |
| Payments | Stripe Checkout (optional) |
Getting started
Prerequisites
- Node.js 20 or newer
- npm
1. Install the project
git clone <your-repository-url>
cd my-carrd
npm install
2. Create the environment file
On macOS or Linux:
cp .env.example .env
On PowerShell:
Copy-Item .env.example .env
The npm development and production scripts run the app on port 3001. Update the three URL values in .env accordingly:
APP_ORIGIN="http://localhost:3001"
NEXT_PUBLIC_SITE_URL="http://localhost:3001"
NEXT_PUBLIC_APP_URL="http://localhost:3001"
Before seeding, replace the default admin password:
SEED_ADMIN_USERNAME="admin"
SEED_ADMIN_PASSWORD="use-a-long-unique-password"
ANALYTICS_SALT="use-a-long-random-value"
Do not commit .env; it contains secrets and is already ignored by Git.
3. Initialize the database
npm run setup
This command generates the Prisma client, applies the schema to SQLite, creates or updates the admin account, and adds starter content when the site is empty. Existing content is preserved unless SEED_OVERWRITE_CONTENT is set to true.
4. Start the application
npm run dev
Open the public site at http://localhost:3001 and the editor at http://localhost:3001/admin.
Environment configuration
The defaults and optional seed settings live in .env.example.
| Variable | Required | Purpose |
|---|---|---|
DATABASE_URL |
Yes | SQLite datasource used by Prisma. The provided relative path is resolved beneath prisma/. |
APP_ORIGIN |
Production | Trusted origin for protected write requests and checkout redirects. |
NEXT_PUBLIC_SITE_URL |
Recommended | Canonical public URL used for metadata and links. |
NEXT_PUBLIC_APP_URL |
Recommended | Public application URL and fallback trusted origin. |
ADMIN_COOKIE_NAME |
No | Admin session cookie name. Defaults to pc_admin. |
ADMIN_COOKIE_TTL_DAYS |
No | Admin session lifetime in days. |
ADMIN_LOGIN_WINDOW_MS |
No | Time window used for login throttling. |
ADMIN_LOGIN_MAX_ATTEMPTS |
No | Failed attempts allowed within the login window. |
ADMIN_LOGIN_BLOCK_MS |
No | Lockout duration after the limit is reached. |
ANALYTICS_SALT |
Yes | Private salt used to derive privacy-minded visitor hashes. |
ANALYTICS_RETENTION_DAYS |
No | Analytics retention period; defaults to 365 days. |
DATABASE_BACKUP_RETENTION |
No | Number of SQLite backups to retain; defaults to 20. |
The SEED_* variables customize the initial admin account and starter page. The seed script accepts additional values for metadata, hero copy, about copy, and the two starter links; see scripts/seed.ts for the complete set.
In production, the seed script refuses to use the example password
change-me-now.
Available commands
| Command | Description |
|---|---|
npm run dev |
Start the development server on port 3001 |
npm run build |
Create a production build |
npm start |
Start the production server on port 3001 |
npm run lint |
Run ESLint |
npm run setup |
Generate Prisma, sync the schema, and seed the site |
npm run seed |
Create/update the admin user and seed missing content |
npm run db:push |
Sync the Prisma schema without creating a migration |
npm run backup |
Create and validate a SQLite snapshot |
npm run maintenance |
Prune expired records and create a scheduled backup |
For a migration-based production deployment, apply committed migrations with:
npx prisma generate
npx prisma migrate deploy
npm run build
npm start
Docker
The repository includes a multi-stage Dockerfile and a Compose configuration with persistent mounts for uploads, Prisma data, and site history.
The application process listens on port 3001. When running the current image, publish the container's port 3001 and set the public URLs to the host-facing address. For example, the Compose port mapping should be 3000:3001 when the site should be available at http://localhost:3000.
After aligning that mapping and creating .env:
docker compose up --build -d
The container syncs the Prisma schema when it starts. Seed a new instance once with:
docker compose exec carrd-clone npm run seed
For production, use a unique admin password and analytics salt, set all public URL variables to the real HTTPS origin, and keep the mounted directories on persistent storage.
Stripe checkout
Stripe is optional; the rest of the application works without it.
- Open Admin → Payments and enter the Stripe publishable key, secret key, and webhook signing secret.
- Enable Stripe and choose the store currency.
- Add a Stripe Price ID to each merchandise item that should be purchasable.
- Register
https://your-domain.example/api/store/webhookas a Stripe webhook endpoint.
Checkout uses Stripe's hosted payment page. Completed sessions are recorded as orders and can be reviewed and marked fulfilled in the admin workspace. Secret values remain server-side and are not returned to the browser after saving.
Data, recovery, and maintenance
Persistent application state is stored in four places:
| Path | Contents |
|---|---|
.env |
Deployment configuration and secrets |
prisma/ |
SQLite database, migrations, and private database backups |
public/uploads/ |
Original uploads and generated image variants |
data/ |
Content version history |
For a complete instance backup, preserve all four locations. Editor history is useful for rolling back content mistakes, but it does not replace a full backup of the database, uploads, and secrets.
You can create and restore database snapshots from Admin → Site settings → Backups. The restore flow verifies SQLite integrity and migration compatibility and creates a recovery snapshot before replacing the live database.
For long-running deployments, schedule:
npm run maintenance
It removes expired sessions, stale login-attempt records, and analytics events older than the configured retention period, then creates a validated database backup.
Project structure
my-carrd/
├── prisma/
│ ├── migrations/ # Committed database migrations
│ └── schema.prisma # Application data model
├── public/uploads/ # Persistent managed media
├── scripts/
│ ├── backup.ts # Manual backup command
│ ├── maintenance.ts # Retention and scheduled backup tasks
│ └── seed.ts # Admin and starter-content seed
└── src/
├── app/ # Pages, layouts, and API routes
├── components/
│ ├── admin/ # Editor, panels, analytics, and order UI
│ └── site/ # Public site renderer and portfolio pages
├── hooks/ # Site-builder state and client hooks
├── lib/ # Auth, data, media, analytics, and validation
├── presets/ # Theme presets
├── templates/ # Section templates
└── types/ # Shared TypeScript models
Production checklist
- Use unique values for
SEED_ADMIN_PASSWORDandANALYTICS_SALT - Set
APP_ORIGIN,NEXT_PUBLIC_SITE_URL, andNEXT_PUBLIC_APP_URLto the HTTPS production origin - Put the application behind a TLS-enabled reverse proxy
- Persist
.env,prisma/,public/uploads/, anddata/ - Run
npx prisma migrate deployduring releases - Schedule
npm run maintenance - Test database restores and off-site backups before relying on them
- Configure and verify the Stripe webhook if payments are enabled
Why I built it
Partner Carrd explores what sits behind a deceptively simple creator website: flexible content modeling, a usable visual editor, secure authentication, upload processing, recoverable persistence, privacy-conscious analytics, and a payment workflow. The result is intentionally small enough to self-host while still demonstrating the engineering concerns of a real, stateful product.
Built with Next.js, TypeScript, Prisma, and a strong preference for owning your content.