Forge is a modern, modular workspace management starter built with Next.js and Appwrite. It provides a solid foundation for building collaborative, feature-rich applications with workspaces, projects, tasks, comments, profiles, and role-based access. Use it as a base to accelerate development of SaaS-style productivity tools.
Contributing: see the CONTRIBUTING.md guide for clone, run, and PR steps.
Status: The app is ~95% complete. It’s a robust boilerplate for building better apps and deeper customizations.
- Workspace-centric app with projects and task tracking.
- Role-based membership (Admin/Member) and secure session auth.
- Clean UI kit (shadcn/ui) and ergonomics for forms, lists, and modals.
- API routes using Hono and type-safe RPC client integration.
- Data layer powered by Appwrite TablesDB with indexes for efficient queries.
- Workspace CRUD with avatars, slugs, and descriptions.
- Member management with role enforcement and leave/delete flows.
- Projects with shortcuts and index constraints per workspace.
- Tasks with assignment, status, due dates, and comments.
- Dashboard summaries for tasks, projects, and members.
- Optimistic updates and React Query caching.
- Theming and responsive UI.
- Frontend: Next.js 15, React 19, TypeScript, Tailwind CSS v4, shadcn/ui.
- Backend: Hono on Edge Runtime, Appwrite SDK (
node-appwrite), Zod validation. - State: React Query; URL-based filters with
nuqs. - Build/Quality: ESLint, Prettier, TypeScript.
- Framework:
Next.js 15(App Router, Edge runtime where applicable) - UI:
React 19,Tailwind CSS v4,shadcn/ui - API & Validation:
Hono,Zod - Data Layer:
Appwrite TablesDBvianode-appwrite - State & Data Fetching:
@tanstack/react-query,nuqsfor URL filters - Tooling:
TypeScript,ESLint(flat config),Prettier
- Linting:
ESLintflat config extends Next core-vitals and TypeScript - Formatting:
Prettierwith organize-imports and Tailwind plugin- Style: single quotes, no semicolons,
trailingComma: "none"
- Style: single quotes, no semicolons,
- Git Hooks:
huskywithlint-staged- Pre-commit runs lint and format on staged files
- Pre-push can run tests or type checks if configured
- Commands:
pnpm lint→ run ESLintpnpm format→ run Prettierpnpm prepare→ ensures Husky is installed (auto-run on install)- Husky hooks located in
.husky/and configured vialint-staged
- Node.js 18+ and
pnpm. - Appwrite account, project, and API key (admin).
- Appwrite Database (TablesDB) and Collections (IDs used via env vars).
This project includes an idempotent setup script to create the Appwrite database, tables, columns, and indexes based on the types in src/types/appwrite.ts.
The script is standalone and does not load env from Next.js. Copy your Appwrite values into the VARIABLES section at the top of the script, run it, and remove the inline secrets afterward. It:
- Ensures the database exists (
DATABASE_ID). - Creates/ensures tables: Workspaces, Members, Projects, Tasks, TaskComments.
- Adds columns and indexes aligned with the codebase.
Run the script:
pnpm tsx src/scripts/create-tables.ts- Go to Appwrite Console: https://appwrite.io/console
- Create a new project (name it “Forge” or similar)
- From Project Settings, copy:
Endpoint(e.g.,https://cloud.appwrite.io/v1)Project ID- Create an API Key with appropriate scopes (server-side admin)
- Create a Database (TablesDB) and note the
Database ID - Decide your table IDs (e.g.,
workspaces,members,projects,tasks,task_comments) — the setup script will ensure their existence
Add these to .env.local (or your deployment environment):
# App URLs
NEXT_PUBLIC_APP_URL=http://localhost:3000
NEXT_PUBLIC_API_URL=http://localhost:3000/api
# Appwrite (Public)
NEXT_PUBLIC_APPWRITE_ENDPOINT=https://<your-appwrite-endpoint>/v1
NEXT_PUBLIC_APPWRITE_PROJECT_ID=<your_project_id>
NEXT_PUBLIC_APPWRITE_DATABASE_ID=<your_database_id>
NEXT_PUBLIC_APPWRITE_WORKSPACES_COLLECTION_ID=<workspaces_table_id>
NEXT_PUBLIC_APPWRITE_MEMBERS_COLLECTION_ID=<members_table_id>
NEXT_PUBLIC_APPWRITE_PROJECTS_COLLECTION_ID=<projects_table_id>
NEXT_PUBLIC_APPWRITE_TASKS_COLLECTION_ID=<tasks_table_id>
NEXT_PUBLIC_APPWRITE_TASK_COMMENTS_COLLECTION_ID=<task_comments_table_id>
# Appwrite (Server/Admin)
NEXT_APPWRITE_KEY=<your_appwrite_api_key>These map to @/config exports used by the app runtime (not by the setup script). The setup script uses inline variables you manually provide.
| Option | Scope | Description | Default |
|---|---|---|---|
DATABASE_ID |
Database | Appwrite database ID to manage | none (required) |
WORKSPACES_COLLECTION_ID |
Table | ID for the workspaces table | none (required) |
MEMBERS_COLLECTION_ID |
Table | ID for the members table | none (required) |
PROJECTS_COLLECTION_ID |
Table | ID for the projects table | none (required) |
TASKS_COLLECTION_ID |
Table | ID for the tasks table | none (required) |
TASK_COMMENTS_COLLECTION_ID |
Table | ID for the task comments table | none (required) |
| Table Permissions | Table | Defaults to read/create/update/delete("users") |
configurable in script |
| Row Security | Table | Row-level security enforcement | true |
| Indexes | Table | Helpful keys and unique constraints | created if missing |
Note: The script is idempotent. It only creates resources if they do not exist.
pnpm installCreate .env.local with the variables listed above for the app runtime. For the setup script, open src/scripts/create-tables.ts, fill the VARIABLES section with your Appwrite values, then bootstrap the database:
pnpm tsx src/scripts/create-tables.tsStart the dev server:
pnpm devOpen http://localhost:3000.
-
Workspace
Common flows include list, get current, create, update, delete, join, and leave. Hooks and server routes enforce role/membership checks.
-
List workspaces for current user
import { useGetWorkspaces } from '@/features/workspaces/hooks/use-current-workspace' const { data: workspaces, isLoading } = useGetWorkspaces()
-
Get current workspace by URL param
import { useGetCurrentWorkspace } from '@/features/workspaces/hooks/use-current-workspace' const { workspace, isLoading } = useGetCurrentWorkspace(workspaceId)
-
Create workspace (adds current user as
ADMIN)import { useCreateWorkspace } from '@/features/workspaces' const { mutate: createWorkspace } = useCreateWorkspace() createWorkspace({ form: { name: 'Forge', description: 'Demo', icon: 'anvil', slug: '' } })
-
Update workspace (ADMIN-only)
import { useUpdateWorkspace } from '@/features/workspaces' const { mutate: updateWorkspace } = useUpdateWorkspace() updateWorkspace({ form: { name: 'Forge v2', description: 'Refined' }, param: { workspaceId } })
-
Delete workspace (ADMIN-only)
import { useDeleteWorkspace } from '@/features/workspaces' const { mutate: deleteWorkspace } = useDeleteWorkspace() deleteWorkspace({ param: { workspaceId } })
-
Join workspace (invite via
slugcode)import { useJoinWorkspace } from '@/features/workspaces/hooks/use-join-workspace' const { mutate: joinWorkspace } = useJoinWorkspace() joinWorkspace({ json: { code: inviteCode }, param: { workspaceId } })
-
Leave workspace
import { useLeaveWorkspace } from '@/features/profile/hooks/use-leave-workspace' const { mutate: leaveWorkspace } = useLeaveWorkspace() leaveWorkspace({ param: { workspaceId } })
-
-
Projects
import { client } from '@/lib/rpc' // Example: create a project via Hono route await client.api.projects.$post({ json: { name: 'Forge', workspaceId } })
-
Tasks
import { useCreateTask, useDeleteTask, useChangeTaskStatus } from '@/features/tasks' const { mutate: createTask } = useCreateTask() createTask({ name: 'Implement docs', projectId, workspaceId, assigneeId })
-
Workspace Forms
import { CreateWorkspacesForm, EditWorkspacesForm, ModalWorkspaceForm } from '@/features/workspaces/components'
-
Task Views
import { ModalTaskEditForm, ModalTaskInfo } from '@/features/tasks'
- AUTH SCREEN
- DASHBOARD VIEW
- PROJECT VIEW
- TASK BOARD VIEW
- Workspace bootstrap: create workspace → invite members → create projects → add tasks.
- Task filtering: use
useTaskFilterswithnuqsfor URL-driven filters. - Role checks: validate membership before CRUD using server routes and middleware.
- Optimistic UI: React Query mutations with
onSuccesscache updates.
- Feature-first organization under
src/features/*(auth, dashboard, members, profile, projects, tasks, workspaces) - API routes with Hono under
src/app/api/*and server handlers inside each feature’sserverdirectory - Shared types under
src/types/*(appwrite.tsmirrors Appwrite schema) - Shared libs under
src/lib/*(appwrite.tsclient factories,middleware.tsauth/session,rpc.tstyped client) - UI primitives under
src/components/ui/*plus layout scaffolding undersrc/components/layout/* - Scripts under
src/scripts/*(e.g.,create-tables.ts) - Path alias
@/*→./src/*defined intsconfig.json
Example directories:
src/
features/
workspaces/
components/
server/
hooks/
schema.ts
tasks/
components/
server/
hooks/
schema.ts
lib/
appwrite.ts
middleware.ts
rpc.ts



