
How to Make an App Like Trello

Building a project management tool like Trello is one of those engineering exercises that looks straightforward until you're knee-deep in real-time sync conflicts, drag-and-drop edge cases, and multi-tenant permission logic. This post walks through the architecture, stack choices, and trade-offs you'll actually face.
What Does "Like Trello" Actually Mean Architecturally?
Trello's core is a Kanban board: boards contain lists, lists contain cards, cards contain everything else. Simple hierarchy. The complexity is in how that hierarchy behaves under concurrent users, real-time updates, and offline edits.
Before writing a line of code, pin down which of these you actually need:
- Real-time collaboration (multiple users editing the same board simultaneously)
- Offline support with sync-on-reconnect
- Drag-and-drop reordering with conflict resolution
- Attachment storage (Trello caps free plans at 10 MB per attachment)
- Activity feeds and audit logs
- Webhooks and third-party integrations
Each one is a non-trivial engineering decision, not a feature checkbox. Offline support alone can triple your backend complexity. If you don't need it, don't build it.
What Tech Stack Should You Use?
There is no universally correct answer, but there are well-understood trade-offs.
Frontend
React is the most common choice here. The component model maps well to boards, lists, and cards. For drag-and-drop, dnd-kit (currently at v6) is the right library, not the older react-beautiful-dnd which Atlassian has effectively deprecated. dnd-kit handles pointer and touch events, supports virtual lists for performance at scale, and gives you full control over the drop animation.
For state management, Zustand or Redux Toolkit both work. Zustand's footprint is smaller and its API is simpler, which matters when your store shape is already complex from board/list/card nesting. If you're already running Redux elsewhere, RTK's createEntityAdapter is genuinely useful for normalising nested board data.
Real-time updates: Socket.IO (v4.x) is the default, but if you're on a cloud-native stack, consider AWS API Gateway WebSockets or Ably. The trade-off is operational simplicity vs. cost. Self-hosting Socket.IO on a single Node process will fail under load without sticky sessions or a Redis adapter.
Backend
Node.js with Fastify (v4) is a reasonable choice for the API layer. Fastify's schema-based validation with @fastify/ajv-compiler catches bad payloads early, and its overhead is measurably lower than Express under benchmarks. If your team is more comfortable with Python, FastAPI with Uvicorn is a comparable option.
For the database, a PostgreSQL-first approach works well. The data model is relational: boards, lists, cards, members, permissions. Use Prisma (v5) or Drizzle ORM for typed queries. If you go with Prisma, be aware its migration engine has known limitations with complex enum changes.
Redis handles two things here: pub/sub for broadcasting WebSocket events across instances, and caching for board state that multiple users read frequently.
File Storage
Don't store attachments in your database. S3-compatible storage (AWS S3, GCS, or MinIO for self-hosted) with pre-signed URLs for direct browser uploads. Set lifecycle policies to clean up orphaned files, or your storage costs will grow silently.
How Do You Handle Real-Time Sync and Conflict Resolution?
This is where most Trello-like apps get into trouble.
The naive approach is last-write-wins: whoever saves last overwrites the previous state. That works for simple text fields. It breaks when two users are reordering cards in the same list simultaneously.
A more reliable approach for card ordering is to use fractional indexing. Instead of storing integer positions (1, 2, 3), you store fractional values (0.25, 0.5, 0.75). When a card is inserted between two others, you calculate the midpoint. The risk is string length growth after many reorders, so you need a periodic re-normalisation job.
For text fields like card descriptions, if you need true concurrent editing (à la Google Docs), you need Operational Transformation or CRDTs. The Yjs library implements a CRDT-based approach and has a Prosemirror binding if you're using a rich text editor. This adds significant complexity. Most teams building a Trello clone do not need it; a simple "last save wins with a conflict warning" is enough for card descriptions.
Broadcast architecture: when a user makes a change, the API commits it to PostgreSQL, publishes the event to a Redis channel, and all connected Socket.IO servers listening to that channel broadcast to relevant board members. Keep your event payloads small. Send the delta, not the full board state.
/// Not sure where to start?
Get the architecture before you commit
Tell us what you're building and we'll map the technical approach, stack, and rough timeline. No cost, no obligation, no sales call required.
Multi-Tenancy, Permissions, and Data Isolation
Trello's permission model has three levels: workspace member, board member (with viewer/editor roles), and card-level assignment. You'll need at least this.
Implement permissions as a policy layer in your API, not in the ORM. A function like canUserEditCard(userId, cardId) that checks membership and role is easy to test in isolation. Baking permission checks into Prisma where clauses works but becomes hard to audit.
For multi-tenancy, row-level security in PostgreSQL (via SET app.current_user_id and RLS policies) is underused and genuinely effective. It moves the isolation guarantee into the database layer, which is harder to accidentally bypass than application-level checks. The trade-off is that RLS adds query planning overhead and complicates raw SQL debugging.
What Does the Infrastructure Look Like at Scale?
A Trello-like app at small scale (under 1,000 concurrent users) runs fine on a single region with:
- 2 Node.js API instances behind a load balancer (sticky sessions for WebSockets)
- 1 PostgreSQL primary with a read replica
- 1 Redis instance (ElastiCache t3.medium or equivalent)
At medium scale, the first bottleneck is usually WebSocket connection limits per instance. Node.js can handle roughly 10,000 concurrent WebSocket connections per process with Socket.IO under typical load, but memory usage grows. Horizontal scaling requires the Redis adapter and careful session routing.
Database write throughput becomes the second bottleneck. Trello-like apps generate a lot of small writes: card moves, checklist updates, label changes. Batch writes where possible and use PostgreSQL LISTEN/NOTIFY sparingly because it does not scale past a few hundred listeners.
Deployment: containerise with Docker, orchestrate with Kubernetes (EKS, GKE, or AKS) if you're already running K8s. If not, AWS ECS with Fargate is operationally simpler and adequate for most teams.
Conclusion
Building a Trello-like app is a solid, well-understood problem. The architecture is mature, the libraries exist, and the pitfalls are documented. The decisions that actually matter are fractional indexing for card order, your WebSocket broadcast strategy, and how early you implement row-level security.
If you're scoping this build, start with a single-board MVP: no real-time collaboration, no offline support, basic card CRUD. Ship that, measure what users actually do, then layer in real-time sync. Don't build offline support unless your users explicitly need it. It will cost you 3 to 4 weeks of engineering time you may never recoup in adoption.
The next step is writing out your data schema. Get the board/list/card/member tables right before touching the frontend, and the rest of the build becomes significantly more predictable.
FAQ
How long does it take to build a Trello-like app? A functional MVP with boards, lists, cards, and basic user authentication takes 8 to 12 weeks for a team of two mid-level engineers. Real-time collaboration adds 3 to 5 weeks. Full feature parity with Trello's free tier, including attachments, labels, due dates, and activity logs, is roughly 5 to 7 months of sustained effort.
Can you build a Trello clone with no-code tools? Yes, tools like Bubble or Softr can produce a basic Kanban board. The ceiling is low: real-time sync, custom permissions, and API integrations quickly hit limits. If you need white-labelling, custom logic, or plan to scale past a few hundred users, a custom build is the more practical path.
What database is best for a project management app? PostgreSQL handles the relational data model well and scales to significant load with proper indexing and read replicas. MongoDB is sometimes chosen for its flexible schema, but the board/list/card hierarchy is inherently relational and benefits from foreign key constraints and transactions, both areas where PostgreSQL is stronger.
How do you handle drag-and-drop card ordering without conflicts? Fractional indexing is the standard approach. Cards store a decimal position value rather than an integer index. Inserting a card between two others computes the midpoint of their positions. This avoids renumbering all cards on every reorder. You'll need a background job to periodically renormalise positions as precision erodes after many inserts.
Do you need WebSockets, or will polling work? Short polling (every 2 to 5 seconds) works for low-concurrency boards and is dramatically simpler to build and debug. WebSockets are worth the complexity once you have multiple users actively editing the same board in real time and polling latency becomes noticeable. Long polling via Server-Sent Events is a reasonable middle ground if your infrastructure doesn't easily support persistent WebSocket connections.
Have a project in mind? Contact Sodio Technologies to discuss your requirements and explore the right technology solution for your business.
/// Work with us
Talk to the engineers who'd build it
You'll get a technical scope, timeline and cost estimate from the people doing the work, not an account manager. In-house team, no subcontracting, since 2016.
