DocsGet StartedQuick Start
Quick Start Guide
Build your first Local-First application with TopGun in less than 5 minutes. This guide will walk you through installation, initialization, and basic data operations.
1. Installation
Install the core client, adapters, and the React hooks package.
terminal
npm install @topgunbuild/client @topgunbuild/adapters @topgunbuild/react 2. Initialization
Create a TopGun client instance, configure persistence, and wrap your app. The client starts immediately—no loading state needed!
src/main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { TopGunClient } from '@topgunbuild/client';
import { IDBAdapter } from '@topgunbuild/adapters';
import { TopGunProvider } from '@topgunbuild/react';
import App from './App';
// 1. Create the adapter and client
const adapter = new IDBAdapter();
const client = new TopGunClient({
// Use wss:// for production (TLS encrypted)
// Use ws:// only for local development
serverUrl: 'ws://localhost:3000', // Optional sync server
storage: adapter
});
// 2. Start the client (non-blocking!)
client.start();
// No loading state needed - writes are queued in memory
// and persisted once IndexedDB is ready
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<TopGunProvider client={client}>
<App />
</TopGunProvider>
</React.StrictMode>,
); 3. Writing Data
TopGun organizes data in Maps. Use getMap() to access a collection and set() to save data.
src/components/AddTodo.tsx
import { useClient } from '@topgunbuild/react';
import { useState } from 'react';
export const AddTodo = () => {
const client = useClient();
const [text, setText] = useState('');
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const id = crypto.randomUUID();
// Access the 'todos' map
const todos = client.getMap('todos');
// Write data
todos.set(id, {
id,
text,
done: false,
createdAt: Date.now()
});
setText('');
};
return (
<form onSubmit={handleSubmit}>
<input
value={text}
onChange={e => setText(e.target.value)}
placeholder="What needs to be done?"
/>
<button type="submit">Add</button>
</form>
);
}; 4. Reading (Live Query)
Use the useQuery hook to subscribe to a map. The component will automatically re-render when data changes locally or comes in from the server.
src/components/TodoList.tsx
import { useClient, useQuery } from '@topgunbuild/react';
interface Todo {
id: string;
text: string;
done: boolean;
}
export const TodoList = () => {
const client = useClient();
// Subscribe to the 'todos' map
const { data: todos } = useQuery<Todo>('todos');
const toggleTodo = (todo: Todo) => {
const todosMap = client.getMap('todos');
// Update partial state by overwriting the object (merging is handled by CRDT if applicable)
// or setting the new state fully
todosMap.set(todo.id, { ...todo, done: !todo.done });
};
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
<label>
<input
type="checkbox"
checked={todo.done}
onChange={() => toggleTodo(todo)}
/>
{todo.text}
</label>
</li>
))}
</ul>
);
}; Non-Blocking Initialization
IndexedDB can take 50-500ms to initialize. TopGun queues write operations in memory and persists them once ready—your UI renders instantly with zero blocking time. If using the simplifiedTopGun API, use await db.waitForReady() only if you need guaranteed persistence before proceeding.Persistence
By usingIDBAdapter, your data is automatically saved to IndexedDB. Even if the user refreshes the page or closes the browser, the state is preserved locally.Production: Use Secure WebSocket (WSS)
In production, always usewss:// instead of ws:// to encrypt data in transit. See the Security Guide for TLS configuration.Optional: Encrypt Local Storage
For sensitive data, wrap your adapter withEncryptedStorageAdapter to encrypt data at rest. See the Client-Side Encryption section in the Security Guide.