Skip to content

Conversation

@KyleAMathews
Copy link
Collaborator

This example app is for demoing how on-demand syncMode allows DB to load data against very large datasets (millions of rows).

WIP still.

claude and others added 28 commits October 21, 2025 18:11
Add detailed planning documentation for porting the LinearLite example
from Electric SQL + PGlite to TanStack Start + TanStack DB.

Key features of the plan:
- Dual-mode architecture (Query polling vs Electric real-time sync)
- User isolation with Better Auth authentication
- Full feature parity with original LinearLite
- 10-phase implementation roadmap over 4 weeks
- Complete code examples for all major components
- Database schema, tRPC API, and collection configurations

Also includes comprehensive TanStack Start guide for reference.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Update the LinearLite porting plan to use TanStack Start's native
server functions (createServerFn) instead of tRPC for a simpler,
more integrated architecture.

Changes:
- Remove tRPC dependencies from installation section
- Replace tRPC routers with server functions using createServerFn
- Update collection configurations to call server functions directly
- Simplify architecture diagram (Server Functions instead of tRPC)
- Add requireAuth utility for protected server functions
- Update all documentation references to server functions
- Remove tRPC from testing strategy and timeline

Benefits:
- Simpler setup with fewer dependencies
- Better integration with TanStack Start
- Type-safe RPC without external dependencies
- Built-in validation with Zod schemas

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Update Full-Text Search section to reference server function
instead of tRPC endpoint.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Create initial structure for linearlite example porting from Electric
SQL to TanStack Start + TanStack DB.

Setup includes:
- Project configuration (vite, tsconfig, drizzle, eslint)
- Package.json with all dependencies (no tRPC)
- Database schema with users, issues, and comments tables
- Seed data script with demo users and issues
- Tailwind CSS configuration
- Docker compose for PostgreSQL
- Environment variable configuration

This is Phase 1 of the porting plan: Project Setup & Infrastructure.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Implement Phase 2-3 of the porting plan: authentication, server
functions, and TanStack DB collections.

Authentication:
- Better Auth setup with Drizzle adapter
- Server-side auth utilities (requireAuth helper)
- Client-side auth hooks (useSession, signIn, signOut)

Server Functions:
- Issues CRUD operations (getAllIssues, createIssue, updateIssue, deleteIssue)
- Comments CRUD operations (getCommentsByIssueId, createComment, deleteComment)
- Full authorization checking for user-owned data
- Zod schema validation for all inputs

Collections:
- Query collections for polling mode (3-second intervals)
- Electric collections for real-time sync
- Mode switcher context to toggle between query and electric
- Mutation handlers for optimistic updates

Utilities:
- useDebounce hook for text input
- cn() utility for className merging

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Implement basic route structure for the LinearLite example.

Routes:
- Root route with ModeProvider wrapper
- Index route (landing page)
- App entry point (main.tsx)

Configuration:
- index.html with proper meta tags
- README with setup instructions and tech stack overview

This provides the foundation for adding authenticated routes
and component pages in the next phase.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit completes the frontend implementation with all necessary components and routes:

Components added:
- LeftMenu: Navigation sidebar with mode switcher
- TopFilter: Filter bar with issue count and sort options
- IssueRow: Single issue row component for list view
- IssueList: Virtualized list of issues with @tanstack/react-virtual
- IssueBoard: Kanban board with drag-and-drop using @dnd-kit
- BoardColumn: Single column in kanban board
- BoardIssueCard: Draggable issue card component
- Editor: TipTap rich text editor with markdown support
- Comments: Comment list and input form
- IssueDetail: Complete issue detail page with debounced auto-save

Routes added:
- _authenticated: Layout wrapper for authenticated pages
- _authenticated/issues: Issues list page
- _authenticated/board: Kanban board page
- _authenticated/issue/$issueId: Issue detail page

All components use TanStack DB collections for reactive data and implement
optimistic updates for instant UI feedback.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Added TipTap table extension dependencies (table-cell, table-header, table-row)
- Added TanStack Start vite plugin for server/client code splitting
- Fixed variable shadowing in issues.ts query callbacks
- Updated ESLint ignore patterns for config files
- Updated lint-staged to exclude config files
- Generated route tree file

Note: Production build is not yet working - requires additional
configuration for TanStack Start's server/client code splitting.
Development mode should work once database is properly set up.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Major refactoring to support SPA mode since TanStack DB doesn't support SSR:

- Created API client module (src/lib/api-client.ts) with fetch-based functions
- Updated collections (query-mode and electric-mode) to use API client instead of server functions
- Removed TanStack Start vite plugins (not needed for pure SPA)
- Simplified vite.config.ts for SPA build
- Added proxy configuration for /api endpoints

The client bundle now builds successfully! Next steps:
- Create Express API server to handle backend endpoints
- Wire up authentication with cookies/sessions
- Test both Query and Electric sync modes

Build output: 1.59MB client bundle (474KB gzipped)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Restored server functions and configured TanStack Start properly for SPA mode:

- Removed API client approach, restored server function imports in collections
- Created router.tsx with router configuration
- Created start.tsx with defaultSsr: false for SPA mode
- Updated vite.config.ts to use tanstackStart plugin with spa.enabled = true
- Following the pattern from examples/react/projects

Note: Build still fails when trying to bundle server functions into client.
This appears to be a TanStack Start plugin configuration issue that needs
further investigation. The plugin should strip server-only code during build
but currently tries to include @tanstack/start/server in the client bundle.

Possible solutions to investigate:
- Check if there's a version mismatch with TanStack Start dependencies
- Verify the plugin is correctly configured for SPA mode
- Consider moving server functions to API routes under src/routes/api/

Development mode should work once we resolve the build configuration.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Complete port of LinearLite to LinearLarge with TanStack Start + TanStack DB:

## File-based Server Routes
- Created clean API endpoints using createFileRoute in src/routes/api/
- Replaced hash-based server function URLs with readable paths:
  - /api/issues, /api/issues/create, /api/issues/update, /api/issues/delete
  - /api/comments, /api/comments/by-issue, /api/comments/create, /api/comments/delete
- All routes use json() helper from @tanstack/react-start

## Query Params Filtering
- Migrated filterState from react-router-dom to TanStack Router
- Implemented useFilterState hook with useSearch and useNavigate
- Wired FilterMenu to TopFilter component with visual filter chips
- Applied filters in IssueList using TanStack DB declarative expressions:
  - Used eq(), inArray(), and() operators instead of JavaScript operators
  - Proper handling of status and priority filters with query compilation
- All Link components preserve userId and username query params

## Draft System
- Implemented optimistic draft editing for IssueModal and Comments
- Direct collection operations instead of manual transactions:
  - Insert draft directly into collection (appears optimistically)
  - Update directly as user types (issuesCollection.update/commentsCollection.update)
  - Delete on cancel, persist on save
- Leverages collection's built-in persistence handlers

## Components
- Created IssueList with virtualization and live query filtering
- Created IssueModal with draft issue creation
- Updated Comments with draft comment system
- Created TopFilter with FilterMenu integration and filter chips
- Created LeftMenu with navigation preserving query params
- Added contextmenu components (FilterMenu, PriorityMenu, StatusMenu)

## Infrastructure
- Complete database schema with Drizzle ORM
- Electric mode and query mode collection implementations
- User context and auth integration
- Seed data for demo users and issues

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Update TanStack Router packages, Tiptap editor, and other dependencies to their latest versions. Include Electric SQL client upgrade and switch to pkg.pr.new for react-db package.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Regenerate lock file after merging origin/main to resolve conflicts and update dependencies to latest compatible versions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
…mizations timeout: 5m 0s

      - Remove board view components and routes, consolidate to single list view
      - Implement infinite scrolling with TanStack Virtual in IssueList
      - Add predicate push-down support for query collections with offset/limit
      - Update API endpoints to support pagination with offset parameter
      - Refactor IssueModal to use traditional form pattern with server confirmation
      - Fix virtualizer to reset scroll position when filters change
      - Improve empty state to always show filter controls
- Add indexes on issues table: user_id, status, priority, created_at, modified
- Add composite index for common query pattern (user_id, status, priority, created_at)
- Add indexes on comments table: issue_id and composite (issue_id, created_at)
- Make seed script configurable via SEED_ISSUE_COUNT environment variable
- Optimize batch sizes for large datasets (1000 rows for >10k issues)
- Add progress reporting with ETA for long-running seeds
- Add usage documentation to seed script

Usage: SEED_ISSUE_COUNT=100000 pnpm db:seed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
…oaders

This refactor introduces a more efficient pattern for managing live queries:

- Created collections.ts with factory functions and caching for both query and electric mode collections
- Created queries.ts with pre-created live query collections for common queries (issue by ID, comments by issue)
- Updated IssueDetail and Comments components to use pre-created query collections instead of ad-hoc queries
- Added route loaders to preload data before rendering:
  - Index route preloads issues list
  - Issue detail route preloads both issue and comments in parallel
- Made ordering button functional in TopFilter to toggle sort direction
- Consolidated collection definitions from separate files into single collections.ts

Benefits:
- Queries are now reusable and cached across components
- Data is preloaded in route loaders for instant rendering
- Better performance with automatic cleanup when collections are disposed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Fixed issues where navigating back from issue detail only showed 7 items
and triggered multiple API refetches by:

- Added gcTime (10min) to QueryClient defaults and useLiveInfiniteQuery
  to prevent premature cache garbage collection
- Fixed preloadIssuesList to preload with exact filters, ordering, and
  limit that match the component's query
- Updated index route loader to pass filter state from search params
  to ensure preloaded data matches the rendered query
- Reset virtualizer scroll only when filters/ordering actually change
  instead of on every search param change

This ensures cached data persists during navigation and the loader
preloads the right data for instant rendering.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
The `vite preview` command doesn't work properly with TanStack Start's
SPA mode build output. This commit adds a Node.js server wrapper that:

- Serves static files from dist/client/
- Routes API requests (/_serverFn/*, /api/*) to the server bundle
- Provides SPA fallback routing to index.html
- Configures TanStack Start to output index.html instead of _shell.html

Now `pnpm build && pnpm serve` runs the full production server with
working server functions and API routes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Fix query observer subscription to process cached results immediately on remount
- Remove aggressive query cleanup that was overriding TanStack Query's gcTime
- Optimize IssueList re-renders by memoizing callbacks and fixing useEffect deps
- Replace manual count fetch with cached QueryCollection pattern
- Add preloading for issue count in route loader
- Fix filters being passed to issue count API

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
…U6kAA7hePGJt

Resolved pnpm-lock.yaml conflict

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Use shared context menus instead of per-row instances to reduce portal bloat
- Add query caching for issues list to prevent duplicate query instances
- Replace dayjs with native Intl.DateTimeFormat and add memoization
- Add loaderDeps to TanStack Router for proper search param tracking
- Prevent unnecessary setWindow calls in useLiveInfiniteQuery
- Normalize filter arrays for stable cache keys across collections

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Ensure loader and component use same filter object structure to prevent duplicate queries.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@changeset-bot
Copy link

changeset-bot bot commented Nov 22, 2025

⚠️ No Changeset found

Latest commit: 45ec6e1

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@KyleAMathews KyleAMathews marked this pull request as draft November 22, 2025 16:10
@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 22, 2025

More templates

@tanstack/angular-db

npm i https://pkg.pr.new/@tanstack/angular-db@891

@tanstack/db

npm i https://pkg.pr.new/@tanstack/db@891

@tanstack/db-ivm

npm i https://pkg.pr.new/@tanstack/db-ivm@891

@tanstack/electric-db-collection

npm i https://pkg.pr.new/@tanstack/electric-db-collection@891

@tanstack/offline-transactions

npm i https://pkg.pr.new/@tanstack/offline-transactions@891

@tanstack/powersync-db-collection

npm i https://pkg.pr.new/@tanstack/powersync-db-collection@891

@tanstack/query-db-collection

npm i https://pkg.pr.new/@tanstack/query-db-collection@891

@tanstack/react-db

npm i https://pkg.pr.new/@tanstack/react-db@891

@tanstack/rxdb-db-collection

npm i https://pkg.pr.new/@tanstack/rxdb-db-collection@891

@tanstack/solid-db

npm i https://pkg.pr.new/@tanstack/solid-db@891

@tanstack/svelte-db

npm i https://pkg.pr.new/@tanstack/svelte-db@891

@tanstack/trailbase-db-collection

npm i https://pkg.pr.new/@tanstack/trailbase-db-collection@891

@tanstack/vue-db

npm i https://pkg.pr.new/@tanstack/vue-db@891

commit: 673a9f3

@github-actions
Copy link
Contributor

Size Change: 0 B

Total Size: 86.3 kB

ℹ️ View Unchanged
Filename Size
./packages/db/dist/esm/collection/change-events.js 1.38 kB
./packages/db/dist/esm/collection/changes.js 977 B
./packages/db/dist/esm/collection/events.js 388 B
./packages/db/dist/esm/collection/index.js 3.24 kB
./packages/db/dist/esm/collection/indexes.js 1.1 kB
./packages/db/dist/esm/collection/lifecycle.js 1.67 kB
./packages/db/dist/esm/collection/mutations.js 2.26 kB
./packages/db/dist/esm/collection/state.js 3.43 kB
./packages/db/dist/esm/collection/subscription.js 2.48 kB
./packages/db/dist/esm/collection/sync.js 2.37 kB
./packages/db/dist/esm/deferred.js 207 B
./packages/db/dist/esm/errors.js 4.19 kB
./packages/db/dist/esm/event-emitter.js 748 B
./packages/db/dist/esm/index.js 2.64 kB
./packages/db/dist/esm/indexes/auto-index.js 742 B
./packages/db/dist/esm/indexes/base-index.js 766 B
./packages/db/dist/esm/indexes/btree-index.js 1.87 kB
./packages/db/dist/esm/indexes/lazy-index.js 1.1 kB
./packages/db/dist/esm/indexes/reverse-index.js 513 B
./packages/db/dist/esm/local-only.js 837 B
./packages/db/dist/esm/local-storage.js 2.1 kB
./packages/db/dist/esm/optimistic-action.js 359 B
./packages/db/dist/esm/paced-mutations.js 496 B
./packages/db/dist/esm/proxy.js 3.22 kB
./packages/db/dist/esm/query/builder/functions.js 733 B
./packages/db/dist/esm/query/builder/index.js 3.96 kB
./packages/db/dist/esm/query/builder/ref-proxy.js 917 B
./packages/db/dist/esm/query/compiler/evaluators.js 1.35 kB
./packages/db/dist/esm/query/compiler/expressions.js 430 B
./packages/db/dist/esm/query/compiler/group-by.js 1.8 kB
./packages/db/dist/esm/query/compiler/index.js 1.96 kB
./packages/db/dist/esm/query/compiler/joins.js 2 kB
./packages/db/dist/esm/query/compiler/order-by.js 1.25 kB
./packages/db/dist/esm/query/compiler/select.js 1.07 kB
./packages/db/dist/esm/query/expression-helpers.js 1.43 kB
./packages/db/dist/esm/query/ir.js 673 B
./packages/db/dist/esm/query/live-query-collection.js 360 B
./packages/db/dist/esm/query/live/collection-config-builder.js 5.26 kB
./packages/db/dist/esm/query/live/collection-registry.js 264 B
./packages/db/dist/esm/query/live/collection-subscriber.js 1.74 kB
./packages/db/dist/esm/query/live/internal.js 130 B
./packages/db/dist/esm/query/optimizer.js 2.56 kB
./packages/db/dist/esm/query/predicate-utils.js 2.88 kB
./packages/db/dist/esm/query/subset-dedupe.js 921 B
./packages/db/dist/esm/scheduler.js 1.21 kB
./packages/db/dist/esm/SortedMap.js 1.18 kB
./packages/db/dist/esm/strategies/debounceStrategy.js 247 B
./packages/db/dist/esm/strategies/queueStrategy.js 428 B
./packages/db/dist/esm/strategies/throttleStrategy.js 246 B
./packages/db/dist/esm/transactions.js 2.9 kB
./packages/db/dist/esm/utils.js 881 B
./packages/db/dist/esm/utils/browser-polyfills.js 304 B
./packages/db/dist/esm/utils/btree.js 5.61 kB
./packages/db/dist/esm/utils/comparison.js 852 B
./packages/db/dist/esm/utils/index-optimization.js 1.51 kB
./packages/db/dist/esm/utils/type-guards.js 157 B

compressed-size-action::db-package-size

@github-actions
Copy link
Contributor

Size Change: 0 B

Total Size: 3.34 kB

ℹ️ View Unchanged
Filename Size
./packages/react-db/dist/esm/index.js 225 B
./packages/react-db/dist/esm/useLiveInfiniteQuery.js 1.17 kB
./packages/react-db/dist/esm/useLiveQuery.js 1.11 kB
./packages/react-db/dist/esm/useLiveSuspenseQuery.js 431 B
./packages/react-db/dist/esm/usePacedMutations.js 401 B

compressed-size-action::react-db-package-size

const query = createLiveQueryCollection((q) =>
q
.from({ issue: issuesCollection })
.where(({ issue }) => eq(issue.id, issueId))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason this is a plain query result and not returned with findOne if we only ever expect one issue to be returned?

@kevin-dp kevin-dp force-pushed the claude/port-linearlite-tanstack-011CULn8TcMQU6kAA7hePGJt branch from 7b2a143 to 45ec6e1 Compare November 25, 2025 08:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants