TechnicalCase StudyNext.jsTauriSupabase

Building Syntora Todo: A Technical Deep Dive

A comprehensive technical breakdown of building a modern desktop task management application with real-time sync, automated workflows, and analytics using Next.js, Tauri, and Supabase.

July 22, 202515 min readBy Parker Gawne

The Problem We Solved

Every productivity tool we tried fell short. They were either too complex, too slow, or didn't integrate with our workflow. So we built our own.

Syntora Todo isn't just a task manager. It's a desktop-native application that combines real-time synchronization, automated workflows, and business analytics into a single, fast experience.

Architecture Overview

We chose a modern stack optimized for speed and developer experience:

  • Frontend: Next.js 15 with React 19
  • Desktop Runtime: Tauri (Rust-based, 10x smaller than Electron)
  • Backend: Supabase (Postgres + Realtime + Auth)
  • State Management: Zustand
  • Styling: Tailwind CSS

Electron apps are notorious for memory bloat. A simple todo app shouldn't consume 500MB of RAM. Tauri compiles to native binaries using the system's webview, resulting in:

  • 10x smaller bundle size (8MB vs 80MB+)
  • 3x lower memory usage
  • Native performance with Rust backend
  • Better security through sandboxing

Real-Time Sync Architecture

The core challenge was keeping tasks synchronized across devices without lag. Here's how we solved it:

When a user creates a task, we don't wait for the server. The UI updates immediately, then syncs in the background.

// Optimistic update pattern
const createTask = async (task: Task) => {
  // 1. Update local state immediately
  setTasks((prev) => [...prev, { ...task, status: 'pending' }]);

  // 2. Sync to server
  const { error } = await supabase.from('tasks').insert(task);

  // 3. Handle failure by rolling back
  if (error) {
    setTasks((prev) => prev.filter((t) => t.id !== task.id));
    toast.error('Failed to create task');
  }
};

For multi-device sync, we subscribe to Postgres changes:

useEffect(() => {
  const channel = supabase
    .channel('tasks')
    .on('postgres_changes',
      { event: '*', schema: 'public', table: 'tasks' },
      (payload) => {
        handleRealtimeUpdate(payload);
      }
    )
    .subscribe();

  return () => { supabase.removeChannel(channel); };
}, []);

Automated Workflows

The real power comes from automation. Users can create workflows that trigger based on task events:

  • Task completed -> Send Slack notification
  • Due date passed -> Escalate priority
  • New task tagged "client" -> Create calendar event

We built a lightweight workflow engine using Supabase Edge Functions:

// Edge function: handle-task-events
Deno.serve(async (req) => {
  const { type, task, old_task } = await req.json();

  // Load user's automation rules
  const rules = await getRulesForUser(task.user_id);

  // Execute matching rules
  for (const rule of rules) {
    if (matchesCondition(rule.condition, type, task, old_task)) {
      await executeAction(rule.action, task);
    }
  }

  return new Response('OK');
});

Analytics Dashboard

We track task completion patterns to help users understand their productivity:

  • Completion rate by day/week
  • Average time to complete by tag
  • Overdue task trends
  • Peak productivity hours

All computed server-side using Postgres functions, not client-side JavaScript. This keeps the app fast regardless of data volume.

Performance Optimizations

For users with thousands of tasks, we use virtual scrolling to render only visible items:

import { useVirtualizer } from '@tanstack/react-virtual';

const rowVirtualizer = useVirtualizer({
  count: tasks.length,
  getScrollElement: () => parentRef.current,
  estimateSize: () => 48,
  overscan: 5,
});

Heavy components load on demand:

const Analytics = dynamic(() => import('./Analytics'), {
  loading: () => <Skeleton />,
  ssr: false,
});

We index the columns we query most:

CREATE INDEX idx_tasks_user_status ON tasks(user_id, status);
CREATE INDEX idx_tasks_due_date ON tasks(due_date) WHERE due_date IS NOT NULL;
CREATE INDEX idx_tasks_created ON tasks(created_at DESC);

Lessons Learned

  1. Start with the data model. We spent a week designing the schema before writing UI code. Worth every hour.

  2. Optimistic updates are essential. Users notice 200ms delays. They don't notice 0ms.

  3. Tauri is production-ready. Despite being newer than Electron, we hit zero blocking issues.

  4. Supabase realtime scales. We tested with 10,000 concurrent connections. No problems.

What's Next

We're adding:

  • AI task suggestions based on patterns
  • Team collaboration with shared projects
  • Mobile app using the same codebase
  • API access for power users

Try It Yourself

Syntora Todo is available for Mac, Windows, and Linux. Built by the team that automates businesses, for our own productivity.

Have questions about our architecture decisions? Contact us or book a free consultation to discuss how we can build something similar for your business.

TechnicalCase StudyNext.jsTauriSupabase
All Articles

Need help implementing this?

We build the automations we write about. Book a free consultation.