--- title: "Signal" slug: "signal" updated: 2026-08-26T07:46:39Z published: 2026-08-26T07:46:39Z canonical: "docs.viewlift.com/signal" --- > ## Documentation Index > Fetch the complete documentation index at: https://docs.viewlift.com/llms.txt > Use this file to discover all available pages before exploring further. # Signal ## Signal Signal is a typed, in-process pub/sub transport for decoupled communication between components, modules, and services within the same browser tab. It wraps PubSubJS with a type-safe topic registry and a static API. ## Scope and Responsibility Signal **is** responsible for: - Publishing domain events within the browser tab - Routing events to subscribers by topic - Enforcing topic-level type safety via `SignalTopicType` Signal is **not** responsible for: - Cross-tab communication (use `BroadcastChannel` for that) - Persisting events - Guaranteeing delivery order - Network communication - Business logic or orchestration ## API ``` import { Signal } from '@/lib/internal/Signal'; // Subscribe — returns a token for targeted unsubscribe const token = Signal.subscribe('loading', (value) => { setLoading(value ?? false); }); // Publish — fires event to all subscribers of the topic Signal.publish('loading', true); // Unsubscribe — removes all subscribers for the topic Signal.unsubscribe('loading'); ``` | Method | Signature | Returns | | --- | --- | --- | | `subscribe` | `subscribe<T>(topic, callback) → string` | PubSubJS subscription token | | `unsubscribe` | `unsubscribe(topic) → void` | — | | `publish` | `publish<T>(topic, data?) → void` | — | All three methods are static. There is no instance state. ## Topic Registry Topics are defined as the `SignalTopicType` union. Adding a new topic requires updating this type — arbitrary strings are rejected at compile time. | Topic | Domain | Payload | Description | | --- | --- | --- | --- | | `loading` | UI | `boolean` | Global loading spinner state | | `notification` | UI | — | Notification trigger | | `upload` | Upload | `{ event: UploadEvent }` | Upload state change (triggers sync) | | `upload:completed` | Upload | `UploadSessionSnapshot` | Upload finished — dispatches to `UploadListener` | | `upload:domainProcessingCompleted-${id}` | Upload | `UploadDomainProcessedEvent` | Domain processing done for a specific upload | | `content:add` | Content | — | Content created | | `content:edit` | Content | — | Content updated | | `content:tab-change` | Content | — | Content tab switched | | `admin::apikey_edit` | Admin | — | API key edited | | `admin::encoding_edit` | Admin | — | Encoding config edited | | `admin::user_edit` | Admin | — | System user edited | | `zipcode-group:edit` | Monetization | — | Zipcode group edited | | `zipcode-group:delete` | Monetization | — | Zipcode group deleted | | `license-group:edit` | Monetization | — | License group edited | | `plan-version` | Monetization | `string` (planId) | Plan version action triggered | | `plan-country:edit` | Monetization | — | Plan country invariant edited | | `plan-country:delete` | Monetization | — | Plan country invariant deleted | | `customer-support:add-user` | Customer Support | — | User added | | `customer-support:delete-device` | Customer Support | — | Device deleted | | `customer-support:refresh-billing-history` | Customer Support | — | Billing history refresh requested | | `auth-user-reset-password-otp-verified` | Auth | `boolean` | OTP verified during password reset | | `auth-user-reset-password-change-success` | Auth | `boolean` | Password change completed | Dynamic topics use template literal types (e.g., `upload:domainProcessingCompleted-${string}`) for per-entity targeting. ## Execution Model ``` Publisher ──publish(topic, data)──► PubSubJS ──callback(data)──► Subscriber(s) ``` - Delivery is synchronous within PubSubJS internals - Multiple subscribers on the same topic all receive the event - `unsubscribe(topic)` removes **all** subscribers for that topic — not a single one - No replay, buffering, or persistence — if no subscriber is listening, the event is dropped ## Common Patterns ### Global Loading State `ClientWrapper` subscribes to `loading` to drive a top-level progress bar. Auth flows publish `loading` to coordinate UI state without prop drilling: ``` // Subscribe (ClientWrapper) Signal.subscribe('loading', (value) => { setLoading(value ?? false); }); // Publish (useAuth) setLoading(() => { Signal.publish('loading', true); return true; }); ``` ### Upload Pipeline The upload system uses Signal as its event bus: 1. `UploadEngine` emits internal events 2. `UploadContext` subscribes to `upload`, detects `UPLOAD_COMPLETED`, and publishes `upload:completed` 3. `UploadListener` subscribes to `upload:completed`, processes the payload, and publishes `upload:domainProcessingCompleted-{id}` 4. The originating component listens for its specific `domainProcessingCompleted` signal ### Entity Refresh Modules publish edit/delete signals to tell sibling components to re-fetch: ``` // After editing a plan country Signal.publish('plan-country:edit'); // Subscriber re-fetches data Signal.subscribe('plan-country:edit', () => { refetch(); }); ``` ## Adding a New Topic 1. Add the topic string to `SignalTopicType` in `src/lib/internal/Signal.ts` 2. Use `Signal.publish<PayloadType>(topic, data)` at the source 3. Use `Signal.subscribe<PayloadType>(topic, callback)` at the consumer 4. Call `Signal.unsubscribe(topic)` in cleanup (typically a `useEffect` return) ## Design Decisions - **Static class wrapping PubSubJS** — avoids instance management while providing a typed API surface over a well-tested pub/sub library - **Typed topic union** — prevents typos and undocumented topics; new topics require a code change in the source of truth - **Topic-level unsubscribe only** — matches the usage pattern where each `useEffect` owns a topic subscription and cleans up on unmount - **No cross-tab delivery** — Signal is for in-tab coordination; cross-tab concerns (e.g., logout broadcast) use a separate `BroadcastChannel` mechanism - **Dynamic template literal topics** — enables per-entity targeting (e.g., per-upload completion) without abandoning type safety