Markdown Converter
Agent skill for markdown-converter
> **Audience:** coding agents/codegen tools
Sign in to like and favorite skills
Audience: coding agents/codegen tools Goal: ship fast, reliable, local-first apps by pairing Electric (Postgres sync engine over HTTP) with TanStack DB (embedded client DB with live queries & optimistic mutations). Status: current as of 2025-09-18.
queryCollectionOptions→electricCollectionOptions without touching components (TanStack)where/columns) (TanStack)SOURCE_SECRET to browser – inject server-side via proxynpx gitpick electric-sql/electric/tree/main/examples/tanstack-db-web-starter my-tanstack-db-project cd my-tanstack-db-project cp .env.example .env pnpm install pnpm dev # in new terminal pnpm migrate
// TanStack Start server function import { createServerFileRoute } from '@tanstack/react-start/server' import { ELECTRIC_PROTOCOL_QUERY_PARAMS } from '@electric-sql/client' const ELECTRIC_URL = 'https://api.electric-sql.cloud/v1/shape' const serve = async ({ request }: { request: Request }) => { const url = new URL(request.url) const origin = new URL(ELECTRIC_URL) // Pass Electric protocol params url.searchParams.forEach((v, k) => { if (ELECTRIC_PROTOCOL_QUERY_PARAMS.includes(k)) origin.searchParams.set(k, v) }) // Server decides shape origin.searchParams.set('table', 'todos') // Tenant isolation: origin.searchParams.set('where', `user_id=$1`) // origin.searchParams.set('params', JSON.stringify([user.id])) origin.searchParams.set('source_id', process.env.SOURCE_ID!) origin.searchParams.set('secret', process.env.SOURCE_SECRET!) const res = await fetch(origin) const headers = new Headers(res.headers) headers.delete('content-encoding') headers.delete('content-length') return new Response(res.body, { status: res.status, statusText: res.statusText, headers, }) } export const ServerRoute = createServerFileRoute('/api/todos').methods({ GET: serve, })
import { createCollection } from '@tanstack/react-db' import { electricCollectionOptions } from '@tanstack/electric-db-collection' import { todoSchema } from './schema' export const todoCollection = createCollection( electricCollectionOptions({ id: 'todos', schema: todoSchema, getKey: (row) => row.id, shapeOptions: { url: '/api/todos' }, onInsert: async ({ transaction }) => { const newTodo = transaction.mutations[0].modified const { txid } = await api.todos.create(newTodo) return { txid } }, // onUpdate/onDelete same pattern }) )
Shape config:
where/columnscolumnsonInsert/onUpdate/onDeleteBackend: get Postgres txid and return as an integer
SELECT pg_current_xact_id()::xid::text as txid
TanStack DB SQL-like queries sub-ms performance differential dataflow (TanStack):
import { useLiveQuery, eq } from '@tanstack/react-db' export function TodoList() { const { data: todos } = useLiveQuery((q) => q .from({ todo: todoCollection }) .where(({ todo }) => eq(todo.completed, false)) .orderBy(({ todo }) => todo.created_at, 'desc') .limit(50) ) return ( <ul> {todos.map((todo) => ( <li key={todo.id}>{todo.text}</li> ))} </ul> ) }
Dependencies:
const [direction, setDirection] = useState('desc') const { data } = useLiveQuery( (q) => q .from({ todo: todoCollection }) .orderBy(({ todo }) => todo.createdAt, direction) .limit(50), [direction] )
Cross-collection joins:
.join({ user: userCollection }, ({ todo, user }) => eq(todo.user_id, user.id)) .where(({ user }) => eq(u.active, true)) .select(({ todo, user }) => ({ id: todo.id, text: todo.text, userName: user.name }))
Aggregations:
.groupBy(({ todo }) => todo.listId) .select(({ todo }) => ({ listId: todo.listId, totalTodos: count(todo.id) }))
function TodoActions() { const handleAdd = () => { todoCollection.insert({ id: crypto.randomUUID(), text: 'New todo', completed: false, createdAt: Date.now(), }) } const handleToggle = (todo) => { todoCollection.update(todo.id, (draft) => { draft.completed = !draft.completed }) } const handleDelete = (todoId) => todoCollection.delete(todoId) }
import { createOptimisticAction } from '@tanstack/react-db' const bootstrapTodoListAction = createOptimisticAction<string>({ onMutate: (listId, itemText) => { listCollection.insert({ id: listId }) todoCollection.insert({ id: crypto.randomUUID(), text: itemText, listId }) }, mutationFn: async (listId, itemText) => { const { txid } = await api.todos.bootstrapTodoList({ listId, itemText }) await Promise.all([ listCollection.utils.awaitTxId(txid), todoCollection.utils.awaitTxId(txid), ]) }, })
shapeOptions: { url: '/api/todos', fetchClient: vi.fn(), // mock fetch onError: (error) => // ... handle fetch errors }
@electric-sql/* & @tanstack/*-dbshapeOptions: { parser: { timestamptz: (date: string) => new Date(date) } }
npm install @tanstack/{angular,react,solid,svelte,vue}-db
import { useLiveQuery } from '...' const { data, isLoading } = useLiveQuery((q) => q.from({ todos: todosCollection }) )
React Native: Requires
react-native-random-uuid + import in entry point
useQuery in Query Collection (queryCollectionOptions)npx @electric-sql/start my-app pnpm claim && pnpm deploy
docker run -e DATABASE_URL=postgres://... electricsql/electric
Docker compose:
name: 'electric-backend' services: postgres: image: postgres:16-alpine environment: POSTGRES_DB: electric POSTGRES_USER: postgres POSTGRES_PASSWORD: password ports: ['54321:5432'] volumes: ['./postgres.conf:/etc/postgresql/postgresql.conf:ro'] tmpfs: ['/var/lib/postgresql/data', '/tmp'] command: ['postgres', '-c', 'config_file=/etc/postgresql/postgresql.conf'] backend: image: electricsql/electric:canary environment: DATABASE_URL: postgresql://postgres:password@postgres:5432/electric?sslmode=disable ELECTRIC_INSECURE: true ports: ['3000:3000'] depends_on: ['postgres']
Postgres requirements: v14+, logical replication, user with REPLICATION role,
wal_level=logical
Old: Bidirectional SQLite sync, handled reads+writes New: Electric (Read-only HTTP streaming from Postgres) + TanStack DB (optimistic writes via API)
Avoid old patterns:
// ❌ OLD (doesn't exist) const { db } = await electrify(conn, schema) await db.todos.create({ text: 'New todo' })
Write path:
todos.insert()→optimistic→onInsert→API→Postgres txid→Electric streams→reconcile→drop optimistic
Prefer TanStack DB collections over lower-level Shape/ShapeStream/useShape APIs.