# Auto Tags Source: https://docs.trysignal.ai/app/auto-tags Create and manage auto tags to automatically categorize sessions # Auto Tags Auto tags automatically analyze and categorize your sessions using AI, helping you quickly identify important patterns and issues. ## What are Auto Tags? Auto tags are AI-powered labels that are automatically applied to sessions based on their content. They help you: * **Identify patterns** - Find sessions with similar characteristics * **Track issues** - Automatically detect bugs, errors, and user frustrations * **Segment users** - Categorize sessions by behavior or outcomes * **Filter sessions** - Quickly find sessions matching specific criteria ## Creating Auto Tags ### Step 1: Navigate to Auto Tags 1. Open the Signal app 2. Click **Auto Tags** in the sidebar 3. You'll see tabs for: * **Enabled** - Your active auto tags * **Built-in** - Pre-configured tags you can enable * **My Tags** - Tags you've created ### Step 2: Create a New Tag 1. Click the **+ Create Tag** button 2. Fill in the tag details: **Name** (required) * A short, descriptive name (e.g., "Payment failure", "Happy customer") * Used in filters and session lists **Description** (required) * A detailed description of what this tag should detect * Examples: * "User expresses frustration or negative sentiment" * "Checkout or billing attempt fails" * "User shows clear positive sentiment" **Output Type** (required) * **Boolean** - Tag is either present or not (true/false) * **String** - Tag has a text value * **Number** - Tag has a numeric value * **Enum** - Tag has one of predefined values * **List\** - Tag can have multiple enum values **Enum Values** (required for enum types) * List of possible values (e.g., \["Low", "Medium", "High"]) * One value per line 3. Click **Create Tag** ### Example: Creating a Boolean Tag ```yaml theme={null} Name: Payment failure Description: Checkout or billing attempt fails. User encounters errors during payment processing. Output Type: Boolean ``` This tag will automatically mark sessions where payment failures occur. ### Example: Creating an Enum Tag ```yaml theme={null} Name: User sentiment Description: Overall sentiment expressed by the user during the session Output Type: Enum Enum Values: - Positive - Neutral - Negative ``` This tag will categorize sessions by sentiment level. ## Managing Auto Tags ### Enable Built-in Tags 1. Go to the **Built-in** tab 2. Browse available tags 3. Click **Enable** on tags you want to use 4. Enabled tags will appear in the **Enabled** tab ### Edit Auto Tags 1. Go to the **Enabled** or **My Tags** tab 2. Click the **Edit** icon (pencil) on a tag 3. Modify the description 4. Click **Save** > **Note:** You can only edit the description. To change the name or output type, create a new tag. ### Delete Auto Tags 1. Go to the **Enabled** or **My Tags** tab 2. Click the **Delete** icon (trash) on a tag 3. Confirm deletion > **Warning:** Deleting a tag removes it from all sessions. Historical data will no longer be tagged. ### View Tag Metrics 1. Click on a tag name to view its details 2. See metrics including: * **Total sessions** tagged * **Trend over time** - Chart showing tag frequency * **Recent sessions** - List of sessions with this tag ## Using Auto Tags ### Filter Sessions by Tag 1. Go to the **Replays** page 2. Use the filter dropdown 3. Select tags to filter by 4. Sessions matching the selected tags will be shown ### View Tagged Sessions 1. Go to the **Auto Tags** page 2. Click on a tag name 3. View all sessions with that tag 4. Click on a session to view its replay ### Export Tagged Sessions 1. Filter sessions by tag 2. Use the export feature to download session data 3. Analyze tagged sessions in external tools ## Best Practices ### Writing Good Descriptions * **Be specific** - Clearly describe what should be detected * **Include context** - Mention relevant user actions or events * **Use examples** - Reference specific scenarios when helpful * **Avoid ambiguity** - Make it clear what qualifies **Good Example:** ``` User encounters a JavaScript error visible in the console or UI. Error messages appear, network requests fail, or the page becomes unresponsive. ``` **Bad Example:** ``` Something goes wrong. ``` ### Choosing Output Types * **Use Boolean** for yes/no categories (e.g., "Bug encountered", "Payment success") * **Use Enum** for categories with multiple options (e.g., "Priority: Low/Medium/High") * **Use String** for free-form text (e.g., "Error message") * **Use Number** for numeric values (e.g., "Cart value", "Page load time") ### Tag Organization * **Group related tags** - Use consistent naming (e.g., "Payment failure", "Payment success") * **Start simple** - Begin with boolean tags, add complexity as needed * **Review regularly** - Check tag accuracy and adjust descriptions ## Common Auto Tags ### User Experience Tags * **Angry user** - User expresses frustration or negative sentiment * **Happy customer** - User shows clear positive sentiment * **Onboarding friction** - User gets stuck during signup or first-run ### Technical Tags * **Bug encountered** - Visible error or broken UI is encountered * **Performance issue** - Slow page loads or laggy interactions * **Network error** - Failed API requests or network problems ### Business Tags * **Payment failure** - Checkout or billing attempt fails * **Churn risk** - Signals indicate potential churn * **Feature request** - User requests a missing capability * **Pricing frustration** - User complains about price or discounts ## Troubleshooting ### Tag Not Appearing on Sessions * **Check description** - Ensure it clearly describes what to detect * **Wait for processing** - Tags are applied as sessions are analyzed * **Review examples** - Check if similar sessions are tagged correctly ### Tag Too Broad or Too Narrow * **Refine description** - Add more specific criteria or examples * **Split tags** - Create separate tags for different scenarios * **Combine tags** - Use filters to combine multiple tags ### Incorrect Tag Values * **Update description** - Clarify what should be detected * **Check enum values** - Ensure all valid options are included * **Review examples** - Verify tag is working as expected # Chat Source: https://docs.trysignal.ai/app/chat Use AI chat to analyze sessions and get insights # Chat The Signal Chat feature uses AI to help you analyze sessions, answer questions, and get insights from your session data. ## Getting Started ### Accessing Chat 1. Open the Signal app 2. Click **Chat** in the sidebar 3. A new chat conversation will start automatically ### Chat Interface The chat interface includes: * **Message input** - Type your questions at the bottom * **Conversation history** - View previous messages and responses * **Session context** - Chat can reference specific sessions * **AI responses** - Get insights and analysis from AI ## Using Chat ### Basic Questions Ask questions about your sessions: ``` How many sessions did we have yesterday? What are the most common errors? Show me sessions with payment failures ``` ### Session Analysis Ask about specific sessions: ``` Analyze session abc123 What happened in session xyz789? Why did the user leave in session def456? ``` ### Trend Analysis Ask about trends and patterns: ``` How has user engagement changed this week? What are the top user actions? Show me conversion trends ``` ### Filtering Sessions Ask to filter sessions: ``` Show me sessions from angry users Find sessions with checkout errors List sessions from last week with bugs ``` ## Chat Features ### Session Context When viewing a session replay: 1. Open a session from the **Replays** page 2. Click **Open in Chat** or navigate to Chat 3. Chat automatically includes session context 4. Ask questions about that specific session ### Conversation History * **Previous chats** - Access past conversations * **Continue conversations** - Pick up where you left off * **New chat** - Start fresh conversations ### AI Insights Chat provides: * **Session summaries** - Quick overviews of sessions * **Error analysis** - Identify and explain errors * **User behavior** - Understand user actions * **Recommendations** - Suggestions for improvements ## Example Queries ### Session Analysis ``` Q: What happened in session abc123? A: [AI analyzes the session and provides a summary] Q: Why did the user leave? A: [AI identifies the reason based on session data] Q: Were there any errors? A: [AI lists all errors encountered] ``` ### Trend Questions ``` Q: How many sessions did we have this week? A: [Shows session count and trend] Q: What's our average session duration? A: [Provides metric and comparison] Q: What are the top 5 events? A: [Lists most frequent events] ``` ### Filtering ``` Q: Show me sessions with payment failures A: [Lists matching sessions with links] Q: Find angry users from last week A: [Filters sessions by auto tags] Q: Sessions with checkout errors A: [Shows relevant sessions] ``` ## Best Practices ### Asking Effective Questions * **Be specific** - "Sessions with payment errors" vs "errors" * **Include context** - Mention time ranges or filters * **Use natural language** - Chat understands conversational queries * **Reference sessions** - Include session IDs when asking about specific sessions ### Using Session Context * **Open sessions first** - View session before asking questions * **Ask follow-ups** - Build on previous responses * **Reference details** - Mention specific events or actions ### Getting Insights * **Ask "why"** - Understand root causes * **Request comparisons** - Compare time periods or segments * **Get recommendations** - Ask for improvement suggestions ## Troubleshooting ### Chat Not Responding * **Check connection** - Ensure you're connected to the internet * **Wait for response** - AI analysis may take a few seconds * **Try rephrasing** - Restate your question differently ### Incorrect Answers * **Provide more context** - Include session IDs or time ranges * **Be more specific** - Narrow down your question * **Check data** - Verify sessions exist and have data ### Session Not Found * **Verify session ID** - Check the session ID is correct * **Check permissions** - Ensure you have access to the project * **Confirm existence** - Verify the session exists ## Tips ### Quick Analysis Use chat for quick insights without navigating multiple pages: ``` "Summarize yesterday's sessions" "Find all checkout errors" "What's our conversion rate?" ``` ### Deep Dives Ask follow-up questions to dive deeper: ``` Q: Show me payment failures A: [Lists sessions] Q: What's the common cause? A: [Analyzes patterns] Q: Which users are affected? A: [Provides user breakdown] ``` ### Comparisons Compare different time periods or segments: ``` "Compare this week to last week" "Show me differences between mobile and desktop" "What changed after the update?" ``` # Dashboards Source: https://docs.trysignal.ai/app/dashboards Create and manage dashboards to visualize your analytics data # Dashboards Dashboards help you visualize and track key metrics from your session data. Create custom dashboards with multiple widgets to monitor your product's health and user behavior. ## What are Dashboards? Dashboards are collections of widgets that display analytics data in visual formats: * **Track metrics** - Monitor key performance indicators * **Visualize trends** - See how metrics change over time * **Compare data** - Analyze different events and properties * **Share insights** - Present data to your team ## Creating a Dashboard ### Step 1: Navigate to Dashboards 1. Open the Signal app 2. Click **Dashboards** in the sidebar 3. Click **+ New Dashboard** ### Step 2: Configure Dashboard 1. Enter a **Dashboard Name** (required) * Choose a descriptive name (e.g., "Product Health", "User Engagement") 2. Enter a **Description** (optional) * Explain what this dashboard tracks 3. Click **Create Dashboard** ### Step 3: Add Widgets After creating a dashboard, you'll see an empty dashboard. Add widgets: 1. Click **+ Add Widget** or **Edit Dashboard** 2. Select a widget type 3. Configure the widget settings 4. Click **Save** ## Widget Types ### Events Trend Shows how events change over time as a line chart. **Configuration:** * **Event Name** (optional) - Filter by specific event * **Group by Day** - Group results by day instead of hour * **Date Range** - Filter by date range **Use Cases:** * Track event volume over time * Monitor specific event trends * Compare event frequencies ### Top Events Displays the most frequent events as a bar chart. **Configuration:** * **Limit** - Number of top events to show (default: 10) * **Date Range** - Filter by date range **Use Cases:** * Identify most common user actions * Find popular features * Track event distribution ### Sessions Over Time Shows session count over time as a line chart. **Configuration:** * **Date Range** - Filter by date range **Use Cases:** * Monitor daily/weekly session volume * Track growth trends * Identify peak usage times ### Average Session Duration Displays average session length over time. **Configuration:** * **Date Range** - Filter by date range **Use Cases:** * Track engagement levels * Monitor session quality * Identify engagement trends ### Unique Users Shows the number of unique users over time. **Configuration:** * **Date Range** - Filter by date range **Use Cases:** * Track user growth * Monitor active users * Measure user retention ### Event Breakdown Breaks down events by a specific property as a bar chart. **Configuration:** * **Property Name** (required) - Property to break down by * **Event Name** (optional) - Filter by specific event * **Limit** - Number of breakdown values to show * **Date Range** - Filter by date range **Use Cases:** * Analyze events by user property * Compare behavior across segments * Identify property patterns ### Funnel Visualizes conversion through a series of steps. **Configuration:** * **Steps** (required) - Array of event names representing funnel steps * **Date Range** - Filter by date range **Example Steps:** ``` 1. page_view 2. signup_started 3. signup_completed 4. first_purchase ``` **Use Cases:** * Track conversion rates * Identify drop-off points * Optimize user flows ## Managing Dashboards ### View Dashboard 1. Go to the **Dashboards** page 2. Click on a dashboard name 3. View all widgets with live data 4. Widgets automatically refresh ### Edit Dashboard 1. Open a dashboard 2. Click **Edit Dashboard** 3. Modify dashboard name or description 4. Add, remove, or configure widgets 5. Click **Save Dashboard** ### Delete Dashboard 1. Open a dashboard 2. Click **Delete Dashboard** 3. Confirm deletion > **Warning:** Deleting a dashboard cannot be undone. ### Duplicate Dashboard 1. Open a dashboard 2. Click **Duplicate** 3. Enter a new name 4. Click **Create** ## Widget Configuration ### Date Ranges All widgets support date range filtering: * **Last 7 days** - Past week * **Last 30 days** - Past month * **Last 90 days** - Past quarter * **Custom range** - Select specific dates ### Refreshing Data * Widgets automatically refresh when you open a dashboard * Data is cached for performance * Click refresh to update manually ### Widget Size * Widgets automatically size to fit the dashboard * Responsive layout adapts to screen size * Multiple widgets per row on larger screens ## Best Practices ### Dashboard Organization * **Group related metrics** - Keep similar widgets together * **Use clear names** - Make dashboard purpose obvious * **Limit widgets** - Too many widgets can be overwhelming * **Focus on key metrics** - Include only important data ### Widget Selection * **Start with basics** - Sessions, events, users * **Add context** - Include breakdowns and funnels * **Track trends** - Use time-series widgets * **Monitor health** - Include error and performance metrics ### Dashboard Naming * **Be descriptive** - "Product Health Dashboard" vs "Dashboard 1" * **Use categories** - "Engineering", "Product", "Growth" * **Include date** - "Q4 2024 Metrics" for time-bound dashboards ## Example Dashboards ### Product Health Dashboard **Widgets:** 1. Sessions Over Time 2. Average Session Duration 3. Top Events 4. Error Events Breakdown **Purpose:** Monitor overall product health and identify issues. ### User Engagement Dashboard **Widgets:** 1. Unique Users 2. Events Trend (filtered by engagement events) 3. Session Duration 4. Funnel (signup → activation → first action) **Purpose:** Track user engagement and conversion. ### Performance Dashboard **Widgets:** 1. Average Session Duration 2. Network Error Breakdown 3. Console Error Trend 4. Page Load Performance **Purpose:** Monitor technical performance and errors. ## Troubleshooting ### Widget Not Loading * **Check date range** - Ensure data exists for selected dates * **Verify event names** - Confirm events are being tracked * **Check permissions** - Ensure you have access to the project ### Incorrect Data * **Verify filters** - Check widget configuration * **Check date range** - Ensure correct time period * **Review events** - Confirm events are being sent correctly ### Missing Widgets * **Add widgets** - Click "Add Widget" in edit mode * **Check widget types** - Ensure widget type is supported * **Verify configuration** - Check widget settings # Session Replays Source: https://docs.trysignal.ai/app/session-replays View and analyze session replays to understand user behavior # Session Replays Session replays let you watch exactly what users did in your application, helping you understand user behavior, debug issues, and improve your product. ## Accessing Session Replays ### Navigate to Replays 1. Open the Signal app 2. Click **Replays** in the sidebar 3. You'll see a list of all recorded sessions ## Session List ### Viewing Sessions The session list shows: * **Session ID** - Unique identifier for each session * **User** - User information (if identified) * **Duration** - How long the session lasted * **Events** - Number of events recorded * **Tags** - Auto tags applied to the session * **Last Updated** - When the session was recorded ### Filtering Sessions Use filters to find specific sessions: 1. **Date Range** - Filter by when sessions occurred 2. **Auto Tags** - Filter by tags (e.g., "Bug encountered", "Payment failure") 3. **User** - Filter by specific user 4. **Search** - Search by session ID or user ID ### Sorting Sessions * **Most Recent** - Latest sessions first * **Oldest First** - Oldest sessions first * **Longest** - Sessions by duration * **Most Events** - Sessions by event count ## Viewing a Session Replay ### Opening a Session 1. Click on a session in the list 2. The session replay opens in a new view 3. Watch the replay of user interactions ### Replay Controls * **Play/Pause** - Control playback * **Speed** - Adjust playback speed (0.5x, 1x, 2x, 4x) * **Timeline** - Jump to specific times * **Events** - View event timeline * **Network** - See network requests * **Console** - View console logs ### Session Information View session details: * **Session ID** - Unique identifier * **User** - User information * **Duration** - Total session length * **Events** - Breakdown of event types * **Tags** - Applied auto tags * **Insights** - AI-generated insights ## Session Insights ### Auto-Generated Insights Each session includes AI-generated insights: * **User Events** - Key actions the user took * **Product Insights** - Product-related observations * **UX Insights** - User experience observations * **Growth Insights** - Growth and conversion insights * **Engineering Insights** - Technical issues and errors ### Auto Tags Sessions are automatically tagged based on: * **User sentiment** - Positive, neutral, or negative * **Issues encountered** - Bugs, errors, failures * **User behavior** - Actions and patterns * **Custom tags** - Tags you've created ## Analyzing Sessions ### Event Timeline View all events in chronological order: * **Page Views** - Page navigation events * **Clicks** - User clicks and interactions * **Network Requests** - API calls and responses * **Console Logs** - JavaScript console output * **Errors** - JavaScript errors and exceptions * **Custom Events** - Events you've tracked ### Network Requests Inspect network activity: * **Request URL** - API endpoint called * **Method** - HTTP method (GET, POST, etc.) * **Status** - Response status code * **Headers** - Request and response headers * **Body** - Request and response bodies * **Timing** - Request duration ### Console Logs View console output: * **Log Levels** - Info, warn, error, debug * **Messages** - Console log messages * **Stack Traces** - Error stack traces * **Timestamps** - When logs occurred ## Using Session Replays ### Debugging Issues 1. **Find error sessions** - Filter by "Bug encountered" tag 2. **Watch the replay** - See what led to the error 3. **Check network** - Inspect failed API calls 4. **Review console** - Check for JavaScript errors 5. **Identify root cause** - Understand what went wrong ### Understanding User Behavior 1. **Watch user flows** - See how users navigate your app 2. **Identify friction** - Find where users struggle 3. **Track conversions** - See what leads to conversions 4. **Analyze drop-offs** - Understand why users leave ### Improving UX 1. **Find pain points** - Identify confusing areas 2. **Test assumptions** - Verify how users actually use features 3. **Optimize flows** - Improve user journeys 4. **Measure impact** - See how changes affect behavior ## Sharing Sessions ### Copy Session Link 1. Open a session 2. Click **Share** or copy the URL 3. Share the link with your team ### Export Session Data 1. Open a session 2. Click **Export** 3. Download session data as JSON ## Best Practices ### Finding Relevant Sessions * **Use tags** - Filter by auto tags to find specific types of sessions * **Filter by date** - Focus on recent sessions * **Search by user** - Find sessions from specific users * **Sort by events** - Find sessions with many events ### Analyzing Sessions * **Watch full replays** - Don't skip to errors, see the full context * **Check multiple sessions** - Look for patterns across sessions * **Review insights** - Use AI-generated insights as starting points * **Correlate events** - Connect user actions with network requests ### Using Insights * **Read all insights** - Don't just focus on errors * **Look for patterns** - Common issues across sessions * **Prioritize issues** - Focus on high-impact problems * **Track improvements** - Monitor if issues are resolved ## Troubleshooting ### Replay Not Loading * **Check session data** - Ensure session has replay data * **Verify permissions** - Ensure you have access to the project * **Try refreshing** - Reload the page ### Missing Events * **Check date range** - Ensure events occurred in the time period * **Verify tracking** - Confirm events are being sent * **Review filters** - Check if filters are excluding events ### Slow Playback * **Reduce speed** - Use slower playback speeds * **Check network** - Ensure good internet connection * **Close other tabs** - Free up browser resources # Configuration Source: https://docs.trysignal.ai/essentials/configuration Required configuration options for Signal JS # Configuration Signal JS requires `apiKey` and `projectId` to send data. All other options have sensible defaults. ## Required Options ```typescript theme={null} import { createSignal } from '@signal-js/browser'; const signal = createSignal({ /** API key for authentication */ apiKey: 'your-api-key', /** Project identifier */ projectId: 'your-project-id', /** * Optional: API endpoint for sending events. * @default https://api.trysignal.ai/ */ // endpoint: 'https://api.trysignal.ai/', }); await signal.start(); ``` ## Optional: Server-Side Correlation If you're tracking events on the server, enable tracing headers to link client and server events: ```typescript theme={null} const signal = createSignal({ apiKey: 'your-api-key', projectId: 'your-project-id', // Enable tracing headers for server correlation addTracingHeaders: true, // or ['api.example.com'] for specific hostnames }); ``` ## Learn More For advanced configuration options, see: * [Privacy & Masking](/essentials/privacy-masking) - Configure data masking and privacy controls * [Network Capture](/essentials/network-capture) - Configure network request capture * [Console Capture](/essentials/console-capture) - Configure console log capture * [Event Tracking](/essentials/event-tracking) - Configure event and page tracking # Console Capture Source: https://docs.trysignal.ai/essentials/console-capture Capture console logs and JavaScript errors automatically # Console Capture Signal JS automatically captures console logs, JavaScript errors, and unhandled promise rejections. ## Configuration ```typescript theme={null} consoleCaptureOptions: { /** Console levels to capture */ levels: ['log', 'info', 'warn', 'error', 'debug'], /** Capture uncaught JavaScript errors (default: true) */ captureErrors: true, /** Capture unhandled promise rejections (default: true) */ captureUnhandledRejections: true, /** Maximum message length (default: 10000) */ maxMessageLength: 10000, /** Maximum number of arguments per log (default: 10) */ maxArgs: 10, /** Mask sensitive data in console logs (default: true) */ maskSensitiveData: true, } ``` ## Captured Events ### Console Log Events ```typescript theme={null} { type: 'console', level: 'error', message: 'Failed to load user', args: ['Failed to load user', '{"userId": 123}'], timestamp: '2024-01-15T10:30:00.000Z', stack: '...', // For errors } ``` ### JavaScript Error Events ```typescript theme={null} { type: 'error', message: 'Uncaught TypeError: Cannot read property...', source: 'https://example.com/app.js', lineno: 42, colno: 15, stack: '...', timestamp: '2024-01-15T10:30:00.000Z', } ``` ### Unhandled Rejection Events ```typescript theme={null} { type: 'unhandled-rejection', message: 'Promise rejected', reason: 'Network error', timestamp: '2024-01-15T10:30:00.000Z', } ``` ## Console Levels Control which console levels are captured: ```typescript theme={null} levels: ['log', 'info', 'warn', 'error', 'debug'] ``` Only logs matching these levels will be captured. For example, to capture only errors and warnings: ```typescript theme={null} levels: ['warn', 'error'] ``` ## Error Capture ### Uncaught Errors Automatically capture uncaught JavaScript errors: ```typescript theme={null} captureErrors: true ``` These are captured with full stack traces, file names, line numbers, and column numbers. ### Unhandled Promise Rejections Capture promises that are rejected without a `.catch()` handler: ```typescript theme={null} captureUnhandledRejections: true ``` ## Message Limits ### Maximum Message Length Limit the length of captured messages to prevent excessive data: ```typescript theme={null} maxMessageLength: 10000 // Default: 10000 characters ``` Messages exceeding this limit will be truncated. ### Maximum Arguments Limit the number of arguments captured per log call: ```typescript theme={null} maxArgs: 10 // Default: 10 arguments ``` Only the first N arguments will be captured. ## Sensitive Data Masking Automatically mask sensitive data in console logs: ```typescript theme={null} maskSensitiveData: true ``` Example: ```javascript theme={null} // Original log: console.log('User credentials:', { password: 'secret123', ssn: '123-45-6789' }); // Captured as: // "User credentials: { password: '***MASKED***', ssn: '***-**-****' }" ``` The SDK automatically detects and masks: * Passwords * Credit card numbers * Social Security Numbers * API keys * JWT tokens * Email addresses (partially) ## Example: Complete Configuration ```typescript theme={null} import { createSignal } from '@signal-js/browser'; const signal = createSignal({ apiKey: 'your-api-key', projectId: 'your-project-id', captureConsole: true, consoleCaptureOptions: { levels: ['log', 'info', 'warn', 'error', 'debug'], captureErrors: true, captureUnhandledRejections: true, maxMessageLength: 10000, maxArgs: 10, maskSensitiveData: true, }, }); await signal.start(); ``` ## Use Cases * **Debug production issues** by seeing console logs from user sessions * **Track JavaScript errors** with full stack traces * **Monitor unhandled rejections** to catch async errors * **Correlate errors** with user actions in session replay * **Debug API issues** by seeing console logs alongside network requests ## Best Practices 1. **Enable error capture** in production to catch unexpected errors 2. **Use appropriate log levels** (`error`, `warn`) for important messages 3. **Avoid logging sensitive data** - rely on automatic masking as a safety net 4. **Set reasonable limits** on message length and arguments to prevent excessive data 5. **Review captured logs** regularly to identify common issues # Event Tracking Source: https://docs.trysignal.ai/essentials/event-tracking Track custom events and page views # Event Tracking Track custom events, page views, and user interactions with Signal JS. ## Custom Events ### Simple Event ```typescript theme={null} signal.capture('button_clicked'); ``` ### Event with Properties ```typescript theme={null} signal.capture('purchase_completed', { amount: 99.99, currency: 'USD', productId: 'prod_123', quantity: 2, }); ``` ### Event with Timestamp ```typescript theme={null} signal.capture('order_shipped', { orderId: 'order_456', }, { timestamp: new Date('2024-01-15T10:00:00Z'), }); ``` ## Super Properties Properties automatically included with every event: ### Register Super Properties ```typescript theme={null} // Register super properties signal.register({ appVersion: '2.1.0', environment: 'production', platform: 'web', }); ``` ### Register Once ```typescript theme={null} // Register only if not already set signal.registerOnce({ firstVisitDate: new Date().toISOString(), }); ``` ### Remove Super Property ```typescript theme={null} // Remove a super property signal.unregister('environment'); ``` ### Get All Super Properties ```typescript theme={null} const props = signal.getSuperProperties(); ``` ## Page Tracking ### Automatic Page Views Enable automatic page view tracking: ```typescript theme={null} const signal = createSignal({ // ... other config capturePageview: true, // Default: true capturePageleave: true, // Default: true }); ``` ### Manual Page View ```typescript theme={null} // Manual page view signal.capturePageview({ path: '/products/123', title: 'Product Details', }); ``` ### Track SPA Navigation For single-page applications, track navigation manually: ```typescript theme={null} // Track SPA navigation window.addEventListener('popstate', () => { signal.capturePageview(); }); // Or with your router router.onRouteChange((route) => { signal.capturePageview({ path: route.path, title: route.title, }); }); ``` ## Recording Controls Control when events are sent: ```typescript theme={null} // Start recording (called automatically on init) await signal.start(); // Pause recording (events still tracked, replay paused) signal.pauseRecording(); // Resume recording signal.resumeRecording(); // Stop recording entirely signal.stopRecording(); // Check recording state signal.isRecording(); // true/false // Get current session ID signal.getSessionId(); // Get current window ID signal.getWindowId(); // Force flush pending events await signal.flush(); // Shutdown SDK gracefully await signal.shutdown(); ``` ## Example: Complete Event Tracking ```typescript theme={null} import { createSignal } from '@signal-js/browser'; const signal = createSignal({ apiKey: 'your-api-key', projectId: 'your-project-id', // Enable automatic page tracking capturePageview: true, capturePageleave: true, }); await signal.start(); // Register super properties signal.register({ appVersion: '2.1.0', environment: 'production', }); // Track custom events function handleButtonClick(buttonId) { signal.capture('button_clicked', { buttonId, page: window.location.pathname, }); } function handlePurchase(amount, productId) { signal.capture('purchase_completed', { amount, productId, currency: 'USD', }); } // Track page views manually (for SPAs) function trackPageView(path, title) { signal.capturePageview({ path, title, }); } ``` ## Event Structure All events follow this structure: ```typescript theme={null} interface SignalEvent { type: string; // Event type name?: string; // Event name timestamp: number; // Unix timestamp (ms) sessionId: string; // Session ID (UUIDv7) windowId: string; // Window/tab ID (UUIDv7) distinctId?: string; // User ID properties?: object; // Event properties event?: object; // Event-specific data (rrweb, network, etc.) } ``` ## Event Types | Type | Description | | ---------------- | -------------------------------------------------- | | `rrweb` | Session replay event (DOM mutations, interactions) | | `$pageview` | Page view event | | `$pageleave` | Page leave event | | `$identify` | User identification | | `$groupidentify` | Group association | | `$performance` | Performance metrics | | `console` | Console log event | | `custom` | Custom tracked event | ## Use Cases * **Track user interactions** - Button clicks, form submissions, etc. * **Monitor conversions** - Purchase events, sign-ups, etc. * **Measure engagement** - Page views, time on page, etc. * **Debug issues** - Track errors and user flows * **Analytics** - Understand user behavior and patterns ## Best Practices 1. **Use descriptive event names** - `purchase_completed` instead of `event1` 2. **Include relevant properties** - Add context that helps with analysis 3. **Use super properties** - Set common properties once, not per event 4. **Track key user actions** - Focus on events that matter for your business 5. **Test your events** - Verify events are being captured correctly # Network Capture Source: https://docs.trysignal.ai/essentials/network-capture Capture network requests and responses with headers and body recording # Network Capture Signal JS automatically captures network requests made via `fetch` and `XMLHttpRequest`, including headers and response bodies. ## Configuration ```typescript theme={null} networkCaptureOptions: { // Record headers (boolean or per-type) recordHeaders: true, // or recordHeaders: { request: true, response: false, }, // Record body (boolean, content-types array, or per-type) recordBody: true, // or recordBody: ['application/json', 'text/plain'], // or recordBody: { request: ['application/json'], response: true, }, // Exclude certain URLs urlDenyList: [ // By default, Signal excludes its own ingestion host: // ['api.trysignal.ai'] /analytics/, /tracking/, 'api.stripe.com', ], // Always redact these headers sensitiveHeaders: [ 'authorization', 'cookie', 'set-cookie', 'x-api-key', ], // Maximum body size to capture in bytes (default: 1MB) payloadSizeLimitBytes: 1000000, // Custom request masking function maskRequestFn: (request) => { // Return null to exclude the request entirely // Modify and return request to include it return request; }, } ``` ## Captured Data Each network request captures: ```typescript theme={null} { url: 'https://api.example.com/users', method: 'POST', status: 200, initiatorType: 'fetch', // or 'xmlhttprequest', 'navigation', etc. startTime: 1234.5, duration: 150, timestamp: '2024-01-15T10:30:00.000Z', // If headers recording is enabled requestHeaders: { 'content-type': 'application/json', 'authorization': '[REDACTED]', // Sensitive headers masked }, responseHeaders: { ... }, // If body recording is enabled requestBody: '{"name":"John"}', responseBody: '{"id":123}', } ``` ## URL Deny List Exclude specific URLs or patterns from capture: ```typescript theme={null} urlDenyList: [ // Default: 'api.trysignal.ai', // Add your own: /analytics\.google\.com/i, // Regex pattern /sentry\.io/i, 'api.stripe.com', // Exact hostname match /^https:\/\/internal\./, // Pattern matching ] ``` ## Sensitive Headers Headers that are automatically redacted: ```typescript theme={null} sensitiveHeaders: [ 'authorization', 'cookie', 'set-cookie', 'x-api-key', 'x-auth-token', 'x-csrf-token', ] ``` ## Body Size Limits Limit the size of captured request/response bodies: ```typescript theme={null} payloadSizeLimitBytes: 1000000, // 1MB default ``` Requests exceeding this limit will have their bodies truncated or excluded. ## Custom Request Masking Use a custom function to mask or exclude requests: ```typescript theme={null} maskRequestFn: (request) => { // Exclude entire request if (request.url.includes('/admin')) { return null; } // Mask sensitive data in body if (request.body && request.body.password) { request.body.password = '***MASKED***'; } // Return modified request return request; } ``` ## Content-Type Filtering Only capture bodies for specific content types: ```typescript theme={null} recordBody: ['application/json', 'text/plain', 'application/xml'] ``` Or configure separately for requests and responses: ```typescript theme={null} recordBody: { request: ['application/json'], response: true, // Capture all response bodies } ``` ## Example: Complete Configuration ```typescript theme={null} import { createSignal } from '@signal-js/browser'; const signal = createSignal({ apiKey: 'your-api-key', projectId: 'your-project-id', captureNetworkRequests: true, networkCaptureOptions: { recordHeaders: { request: true, response: true, }, recordBody: ['application/json', 'text/plain'], urlDenyList: [ /analytics\.google\.com/i, /sentry\.io/i, ], sensitiveHeaders: [ 'authorization', 'cookie', 'x-api-key', ], payloadSizeLimitBytes: 1000000, maskRequestFn: (request) => { // Exclude admin endpoints if (request.url.includes('/admin')) { return null; } return request; }, }, }); await signal.start(); ``` ## Use Cases * **Debug API errors** by seeing the exact request/response that caused issues * **Monitor API performance** with request duration tracking * **Track user flows** by correlating network requests with user actions * **Debug authentication** issues by inspecting headers (with sensitive data masked) * **Analyze payload sizes** to optimize API responses # Privacy & Masking Source: https://docs.trysignal.ai/essentials/privacy-masking Protect sensitive data with automatic masking and privacy controls # Privacy & Masking Signal JS automatically protects sensitive data at multiple levels with built-in privacy controls. ## DOM Element Masking Use CSS classes or data attributes to control what gets recorded: ```html theme={null}
This content will not appear in recordings
Sensitive text here
Blocked content
Masked content ``` ## Input Field Masking By default, input values are not masked (except passwords). Set `maskAllInputs: true` to mask all inputs, or configure per input type: ```typescript theme={null} sessionRecordingMasking: { maskAllInputs: true, // Set to true to mask all inputs (default: false) maskInputOptions: { password: true, // Always masked email: true, // Masked by default tel: true, // Masked by default text: true, // Masked by default textarea: true, // Masked by default number: false, // Not masked search: false, // Not masked checkbox: false, // Safe (only checked state) radio: false, // Safe (only selected state) }, } ``` ## Automatic Sensitive Data Detection The SDK automatically detects and masks common sensitive data patterns: | Data Type | Pattern | Masked As | | ------------ | ------------------------------------------- | ------------------------ | | Credit Cards | 4111 1111 1111 1111 | `****-****-****-****` | | SSN | 123-45-6789 | `***-**-****` | | Emails | [user@example.com](mailto:user@example.com) | `***@example.com` | | API Keys | sk\_live\_abc123... | `sk_live_***REDACTED***` | | JWT Tokens | eyJhbG... | `Bearer ***TOKEN***` | | Passwords | (in objects) | `***MASKED***` | ## Console Log Masking Sensitive data in console logs is automatically masked: ```javascript theme={null} // Before masking: console.log('User credentials:', { password: 'secret123', ssn: '123-45-6789' }); // Captured as: // "User credentials: { password: '***MASKED***', ssn: '***-**-****' }" ``` ## Network Body Masking Request and response bodies are automatically sanitized: ```javascript theme={null} // Original request body: { email: 'john@example.com', password: 'secret', card: '4111111111111111' } // Captured as: { email: '***@example.com', password: '***MASKED***', card: '****-****-****-****' } ``` ## Programmatic Control Control recording programmatically: ```typescript theme={null} // Temporarily pause recording signal.pauseRecording(); // Resume recording signal.resumeRecording(); // Check if recording is active if (signal.isRecording()) { console.log('Recording is active'); } // Stop recording entirely signal.stopRecording(); ``` ## Network Request Masking Customize network request masking: ```typescript theme={null} networkCaptureOptions: { // Always redact these headers sensitiveHeaders: [ 'authorization', 'cookie', 'x-api-key', 'x-auth-token', ], // Custom request masking function maskRequestFn: (request) => { // Return null to exclude the request entirely if (request.url.includes('sensitive-endpoint')) { return null; } // Modify and return request to include it if (request.body) { request.body = maskSensitiveData(request.body); } return request; }, } ``` ## Best Practices 1. **Use CSS classes** for static elements that should always be masked 2. **Use data attributes** for dynamic content or when CSS classes aren't feasible 3. **Enable automatic masking** for inputs by setting `maskAllInputs: true` (default is `false`) 4. **Configure sensitive headers** to always redact authentication tokens 5. **Use custom masking functions** for complex scenarios 6. **Test your masking** to ensure sensitive data is properly protected # User Identification Source: https://docs.trysignal.ai/essentials/user-identification Identify users and associate them with groups # User Identification Identify users and associate them with groups for better analytics and session tracking. ## Identify Users ### Basic Identification ```typescript theme={null} // Identify with ID and properties signal.identify('user-123', { email: 'user@example.com', name: 'John Doe', plan: 'premium', createdAt: '2024-01-01', }); ``` ### Set Additional Properties ```typescript theme={null} // Set additional properties later signal.setPersonProperties({ lastLogin: new Date().toISOString(), loginCount: 5, }); ``` ### Set Properties Once ```typescript theme={null} // Set properties only if not already set signal.setPersonPropertiesOnce({ firstSeen: new Date().toISOString(), }); ``` ### Get Current User ID ```typescript theme={null} const userId = signal.getDistinctId(); ``` ### Reset Identity ```typescript theme={null} // Reset identity (on logout) signal.reset(); ``` ## Group Analytics Associate users with companies, teams, or other groups: ### Associate with a Company ```typescript theme={null} signal.group('company', 'acme-corp', { name: 'Acme Corporation', plan: 'enterprise', employeeCount: 500, industry: 'Technology', }); ``` ### Multiple Group Types ```typescript theme={null} // Associate with multiple group types signal.group('team', 'engineering'); signal.group('project', 'signal-sdk'); ``` ### Get All Groups ```typescript theme={null} const groups = signal.getGroups(); // { company: 'acme-corp', team: 'engineering', project: 'signal-sdk' } ``` ## Bootstrap with Initial Data Set initial user data when initializing the SDK: ```typescript theme={null} const signal = createSignal({ apiKey: 'your-api-key', projectId: 'your-project-id', bootstrap: { distinctId: 'user-123', superProperties: { plan: 'enterprise' }, }, }); ``` ## Example: Complete User Flow ```typescript theme={null} import { createSignal } from '@signal-js/browser'; const signal = createSignal({ apiKey: 'your-api-key', projectId: 'your-project-id', }); await signal.start(); // On user login async function handleLogin(user) { // Identify the user signal.identify(user.id, { email: user.email, name: user.name, plan: user.plan, }); // Associate with company if (user.company) { signal.group('company', user.company.id, { name: user.company.name, plan: user.company.plan, }); } // Set additional properties signal.setPersonProperties({ lastLogin: new Date().toISOString(), }); } // On user logout function handleLogout() { signal.reset(); } ``` ## Use Cases * **Track user behavior** across sessions by identifying users * **Group analytics** by associating users with companies or teams * **Personalize experiences** by storing user properties * **Segment users** based on properties like plan, industry, etc. * **Track user journeys** from anonymous to identified ## Best Practices 1. **Identify users early** - Call `identify()` as soon as you know who the user is 2. **Use consistent IDs** - Use the same ID format across your application 3. **Store useful properties** - Include properties that help with analytics and segmentation 4. **Reset on logout** - Call `reset()` when users log out to start fresh 5. **Use groups** - Associate users with companies/teams for better analytics # Signal App Overview Source: https://docs.trysignal.ai/getting-started/signal-app Learn the basics of using the Signal app to analyze sessions and get insights # Signal App Overview The Signal app is a web application that helps you analyze user sessions, track metrics, and get insights from your session data. Once you've integrated the Signal SDK and started recording sessions, you can use the Signal app to view replays, create dashboards, and understand user behavior. ## What is the Signal App? The Signal app provides: * **Session Replays** - Watch recordings of user sessions * **Dashboards** - Visualize analytics and track metrics * **Auto Tags** - Automatically categorize sessions with AI * **Chat** - Ask questions and get AI-powered insights * **Analytics** - Track events, users, and conversions ## Accessing the Signal App ### Sign In 1. Navigate to your Signal app URL (provided by your administrator) 2. Sign in with your credentials 3. Select or create a project ### Projects Projects organize your sessions and data: * **Create a project** - Set up a new project for your application * **Switch projects** - Use the project switcher in the sidebar * **Project settings** - Configure project-specific settings ## Key Features ### Session Replays Watch exactly what users did in your application: * **View recordings** - See user interactions, clicks, and navigation * **Inspect events** - View all events in chronological order * **Network requests** - See API calls and responses * **Console logs** - View JavaScript console output * **Filter sessions** - Find sessions by tags, date, or user **Learn more:** [Session Replays Guide](/app/session-replays) ### Dashboards Create visual dashboards to track metrics: * **Widgets** - Add charts and visualizations * **Metrics** - Track events, sessions, users, and more * **Trends** - See how metrics change over time * **Funnels** - Visualize conversion flows * **Customize** - Build dashboards for your needs **Learn more:** [Dashboards Guide](/app/dashboards) ### Auto Tags Automatically categorize sessions with AI: * **Create tags** - Define what to detect (e.g., "Payment failure", "Happy customer") * **AI analysis** - Sessions are automatically tagged * **Filter sessions** - Find sessions by tags * **Track patterns** - Identify common issues or behaviors **Learn more:** [Auto Tags Guide](/app/auto-tags) ### Chat Get AI-powered insights about your sessions: * **Ask questions** - Query your session data * **Analyze sessions** - Get summaries and insights * **Find patterns** - Discover trends and issues * **Get recommendations** - Receive improvement suggestions **Learn more:** [Chat Guide](/app/chat) ## Basic Workflow ### 1. Record Sessions First, integrate the Signal SDK in your application: ```typescript theme={null} import { createSignal } from '@signal-js/browser'; const signal = createSignal({ apiKey: 'your-api-key', projectId: 'your-project-id', }); await signal.start(); ``` Sessions will automatically start recording. ### 2. View Sessions 1. Open the Signal app 2. Go to **Replays** 3. Browse recorded sessions 4. Click on a session to watch the replay ### 3. Analyze Sessions * **Watch replays** - See what users did * **Check insights** - Read AI-generated insights * **Review tags** - See auto tags applied * **Inspect events** - View event timeline ### 4. Create Dashboards 1. Go to **Dashboards** 2. Click **+ New Dashboard** 3. Add widgets to track metrics 4. Monitor your key performance indicators ### 5. Set Up Auto Tags 1. Go to **Auto Tags** 2. Create tags for important patterns 3. Enable built-in tags 4. Sessions will be automatically tagged ## Key Concepts ### Sessions A session represents a single user visit: * **Session ID** - Unique identifier * **Duration** - How long the session lasted * **Events** - All actions recorded during the session * **User** - User information (if identified) ### Events Events are actions that occur during a session: * **Page views** - Page navigation * **Clicks** - User interactions * **Network requests** - API calls * **Console logs** - JavaScript output * **Custom events** - Events you track ### Auto Tags Tags automatically categorize sessions: * **Boolean tags** - Present or not (e.g., "Bug encountered") * **Enum tags** - Categories (e.g., "Priority: Low/Medium/High") * **String tags** - Text values * **Number tags** - Numeric values ### Dashboards Dashboards visualize your data: * **Widgets** - Individual charts or visualizations * **Metrics** - Data points you're tracking * **Filters** - Date ranges and other filters * **Layout** - How widgets are arranged ## Getting Started Checklist * [ ] **Integrate SDK** - Add Signal SDK to your application * [ ] **Configure SDK** - Set endpoint, API key, and project ID * [ ] **Start recording** - Sessions begin recording automatically * [ ] **Access Signal app** - Sign in to the web application * [ ] **View first session** - Watch a session replay * [ ] **Create a dashboard** - Set up your first dashboard * [ ] **Set up auto tags** - Create tags for important patterns * [ ] **Explore chat** - Ask questions about your sessions ## Next Steps Now that you understand the basics: * **Learn about Session Replays** - [View detailed guide](/app/session-replays) * **Set up Dashboards** - [Create your first dashboard](/app/dashboards) * **Configure Auto Tags** - [Create and manage tags](/app/auto-tags) * **Use Chat** - [Get AI insights](/app/chat) ## Tips for Success ### Start Simple * **Begin with replays** - Watch a few sessions to understand user behavior * **Add basic tags** - Create 2-3 important tags first * **Create one dashboard** - Focus on key metrics * **Explore gradually** - Learn features as you need them ### Focus on Value * **Track what matters** - Focus on metrics that impact your business * **Tag important patterns** - Create tags for issues you want to track * **Monitor trends** - Use dashboards to spot changes over time * **Act on insights** - Use findings to improve your product ### Best Practices * **Review sessions regularly** - Check sessions daily or weekly * **Update tags** - Refine tag descriptions based on results * **Share dashboards** - Keep your team informed * **Use chat** - Ask questions to discover insights ## Getting Help * **Documentation** - Check the guides for each feature * **Chat** - Use the chat feature to ask questions * **Support** - Contact support for technical issues # Signal JS Source: https://docs.trysignal.ai/index A modern SDK for session replay, network monitoring, and analytics Signal JS is a comprehensive SDK for session replay, network monitoring, and analytics. Record user sessions, capture network requests, monitor console logs, and track user behavior with built-in privacy protection. ## Features * **Session Replay** - Record and replay user sessions using rrweb * **Network Capture** - Intercept fetch/XHR with headers and body recording * **Console Capture** - Capture console logs and JavaScript errors * **Privacy & Masking** - Automatic masking of sensitive data (PII, credit cards, passwords) * **User Identification** - Identify users with distinct IDs and traits * **Group Analytics** - Associate users with companies/organizations * **Super Properties** - Properties included with every event * **Session Management** - UUIDv7, configurable timeouts, activity tracking * **Storage Layer** - Cookie, localStorage, sessionStorage with fallback chain * **Compression** - Gzip compression for optimized payload sizes * **TypeScript** - Full type definitions included ## Installation Choose the package that matches your framework: | Framework | Package | Installation | | --------------------- | -------------------- | -------------------------------- | | **Vanilla JS / HTML** | `@signal-js/browser` | `npm install @signal-js/browser` | | **Vue.js** | `@signal-js/browser` | `npm install @signal-js/browser` | | **React** | `@signal-js/react` | `npm install @signal-js/react` | | **Next.js** | `@signal-js/nextjs` | `npm install @signal-js/nextjs` | | **Node.js Server** | `@signal-js/node` | `npm install @signal-js/node` | > **Note:** > > * Framework packages (`@signal-js/react`, `@signal-js/nextjs`) automatically include the browser SDK (`@signal-js/browser`). You only need to install one package. > * For server-side tracking with Next.js, `@signal-js/nextjs` includes both client and server SDKs. ## Get Started Get up and running with Signal JS in minutes. Install the SDK and start recording sessions. Learn about all available configuration options for session recording, privacy masking, and more. Protect sensitive data with automatic masking and privacy controls. Learn the basics of using the Signal app to analyze sessions and get insights. # Alias Source: https://docs.trysignal.ai/integrations/nextjs/alias Link an alias to a user (client and server) # Next.js — Alias Link an alias (e.g. anonymous ID) to a user. Use this to merge anonymous events (from before login) with the identified user. Available on both client and server. ## Client: useSignal Get `alias` from `useSignal()`: ```tsx theme={null} 'use client'; import { useSignal } from '@signal-js/nextjs/client'; import { useEffect, useState } from 'react'; function LoginHandler({ userId, userTraits }) { const { identify, alias } = useSignal(); const [anonymousId, setAnonymousId] = useState(null); useEffect(() => { // Store anonymous ID before login const stored = localStorage.getItem('anonymous_id'); if (stored) { setAnonymousId(stored); } }, []); const handleLogin = () => { // Identify the user identify(userId, userTraits); // Link anonymous ID if it exists if (anonymousId) { alias(anonymousId); localStorage.removeItem('anonymous_id'); } }; return ; } ``` ## Client: alias(alias) Links an alias to the current user. Sends a `$create_alias` event. | Parameter | Type | Required | Description | | --------- | -------- | -------- | ------------------------------------------ | | `alias` | `string` | yes | Alias to link (e.g. previous anonymous ID) | **When to use:** After login to merge anonymous events (from before login) with the identified user. ## Server: alias(distinctId, alias, sessionId?, windowId?) Link an alias to a user from the server. Pass `sessionId` and `windowId` for client correlation. | Parameter | Type | Required | Description | | ------------ | -------- | -------- | -------------------------------------------------------- | | `distinctId` | `string` | yes | Current user identifier (e.g. after login) | | `alias` | `string` | yes | Alias to link (e.g. previous anonymous ID) | | `sessionId` | `string` | no | From `x-signal-session-id` header for client correlation | | `windowId` | `string` | no | From `x-signal-window-id` header for client correlation | ```typescript theme={null} // app/api/auth/login/route.ts import { signal, getSessionInfo } from '@/lib/signal-server'; import { NextRequest } from 'next/server'; export async function POST(request: NextRequest) { const { sessionId, windowId, distinctId } = getSessionInfo(request); const body = await request.json(); // Identify the user signal.identify({ distinctId: body.userId, properties: { email: body.email, name: body.name }, sessionId, windowId, }); // Link anonymous ID if provided if (body.anonymousId) { signal.alias(body.userId, body.anonymousId, sessionId, windowId); } return Response.json({ success: true }); } ``` ## How it works 1. **Before login:** User browses anonymously. Events are tracked with an anonymous ID. 2. **After login:** Call `identify(userId, traits)` to set the user's identity. 3. **Link alias:** Call `alias(anonymousId)` to link the anonymous ID to the identified user. 4. **Result:** All events (both anonymous and identified) are now associated with the same user profile. ## See also * [Identify](/docs/integrations/nextjs/identify) — identify() and reset() * [Capture](/docs/integrations/nextjs/capture) — Track events * [Server tracking](/docs/integrations/nextjs/server-tracking) — Server-side patterns # Capture Source: https://docs.trysignal.ai/integrations/nextjs/capture Track events in Next.js (client and server) # Next.js — Capture Track custom events from client components (using React hooks) and from the server (API routes, Server Actions). Pass `sessionId` and `windowId` from request headers on the server to correlate with the client session. ## Client: useSignalCapture or useSignal In any client component, use the same hooks as the [React integration](/docs/integrations/react/capture): ```tsx theme={null} 'use client'; import { useSignalCapture } from '@signal-js/nextjs/client'; export function CheckoutButton({ orderId, amount }) { const capture = useSignalCapture(); return ( ); } ``` You can also use `useSignal()` for full SDK access. Both are re-exported from `@signal-js/nextjs/client`. ## Server: API route (App Router) Import your server client and `getSessionInfo`, then call `signal.capture()` with the session identifiers from the request: ```typescript theme={null} // app/api/checkout/route.ts import { signal, getSessionInfo } from '@/lib/signal-server'; import { NextRequest } from 'next/server'; export async function POST(request: NextRequest) { const { sessionId, windowId, distinctId } = getSessionInfo(request); const body = await request.json(); const orderId = await processOrder(body); signal.capture({ distinctId: distinctId || body.userId, event: 'order_processed', properties: { orderId, amount: body.amount }, sessionId, windowId, }); return Response.json({ success: true, orderId }); } ``` ## Server: Server Action Read headers with `headers()` from `next/headers` and pass them into `signal.capture()`: ```typescript theme={null} // app/actions.ts 'use server'; import { signal, getSessionInfo } from '@/lib/signal-server'; import { headers } from 'next/headers'; export async function processOrderAction(orderData: OrderData) { const headersList = headers(); const sessionId = headersList.get('x-signal-session-id') || undefined; const windowId = headersList.get('x-signal-window-id') || undefined; const distinctId = headersList.get('x-signal-distinct-id') || undefined; const orderId = await processOrder(orderData); signal.capture({ distinctId: distinctId || orderData.userId, event: 'order_processed', properties: { orderId, amount: orderData.amount }, sessionId, windowId, }); return { success: true, orderId }; } ``` For Pages Router API routes and `getServerSideProps`, see [Server tracking](/docs/integrations/nextjs/server-tracking). ## See also * [Identify](/docs/integrations/nextjs/identify) — User identity on client and server * [Server tracking](/docs/integrations/nextjs/server-tracking) — Pages Router and getSessionInfo patterns # Configuration Source: https://docs.trysignal.ai/integrations/nextjs/configuration SignalNextProvider and createSignalServer options for Next.js # Next.js — Configuration Configure the client provider (`SignalNextProvider`) and the server client (`createSignalServer`). Client `options` are the same browser SDK options as in the [React configuration](/docs/integrations/react/configuration). ## SignalNextProvider (client) | Prop | Type | Default | Description | | ------------------- | --------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `options` | `SignalOptions` | required | Browser SDK config. Required: `apiKey`, `projectId`. Optional: `endpoint` (defaults to `https://api.trysignal.ai/`). See [React configuration](/docs/integrations/react/configuration) for full options. | | `trackPageViews` | `boolean` | `true` | Automatically track `$pageview` on route changes | | `trackSearchParams` | `boolean` | `false` | Include URL search params in page view events (requires Suspense boundary) | | `autoStart` | `boolean` | `true` | Start recording when the provider mounts | ## createSignalServer (server) | Option | Type | Default | Description | | ---------------- | -------------- | --------------------------- | -------------------------------------- | | `endpoint` | `string` | `https://api.trysignal.ai/` | API endpoint for sending events | | `apiKey` | `string` | required | API key for authentication | | `projectId` | `string` | `'default'` | Project ID | | `disabled` | `boolean` | `false` | When `true`, returns a no-op client | | `debug` | `boolean` | `false` | Enable debug logging | | `flushBatchSize` | `number` | `20` | Flush when this many events are queued | | `flushInterval` | `number` | `10000` | Flush interval in ms | | `timeout` | `number` | `30000` | Request timeout in ms | | `compression` | `boolean` | `true` | Enable gzip compression | | `fetch` | `typeof fetch` | `globalThis.fetch` | Custom fetch implementation | You must call `createSignalServer()` once (e.g. in `lib/signal-server.ts`) before using the server helpers below. The helpers use the same singleton client. ## Server helpers These utilities are exported from `@signal-js/nextjs/server`. They work with the client created by `createSignalServer()`. ### trackServerEvent(options) Fire a single event from an API route or Server Action without importing your shared `signal` client. Uses the singleton from `createSignalServer`; call `createSignalServer()` at least once (e.g. in `lib/signal-server.ts`) before using. | Option | Type | Required | Description | | ------------ | ------------------------- | -------- | ---------------- | | `distinctId` | `string` | yes | User identifier | | `event` | `string` | yes | Event name | | `properties` | `Record` | no | Event properties | **When to use:** One-off event tracking in API routes or Server Actions when you don't need to pass `sessionId`/`windowId` for correlation. For session correlation, use your shared `signal` client and [getSessionInfo](/docs/integrations/nextjs/server-tracking) instead. ```typescript theme={null} // app/api/checkout/route.ts or pages/api/checkout.ts import { trackServerEvent } from '@signal-js/nextjs/server'; export async function POST(request: Request) { const body = await request.json(); // ... process checkout ... await trackServerEvent({ distinctId: body.userId, event: 'checkout_completed', properties: { amount: body.amount, orderId: body.orderId }, }); return Response.json({ success: true }); } ``` ### getSignalServerSideProps(getServerSidePropsFunc, options?) Wrap your Pages Router `getServerSideProps` to inject Signal-related props (`distinctId`, `sessionId`) into the page. Your wrapper runs first; then Signal props are merged into the returned `props`. | Option | Type | Description | | --------------- | ---------------------------------- | ------------------------------------------------------------------------------ | | `getDistinctId` | `(context) => string \| undefined` | Return the user's distinct ID from the request (e.g. from cookies or session). | | `getSessionId` | `(context) => string \| undefined` | Optional. Return the session ID (e.g. from headers) for correlation. | **When to use:** Pages Router when you want Signal context (e.g. `distinctId`, `sessionId`) available as page props for client components or for tracking. ```typescript theme={null} // pages/dashboard.tsx import { getSignalServerSideProps } from '@signal-js/nextjs/server'; export const getServerSideProps = getSignalServerSideProps( async (context) => { // Your existing data fetching const data = await fetchDashboardData(context.req); return { props: { data } }; }, { getDistinctId: (context) => context.req.cookies.userId, getSessionId: (context) => context.req.headers['x-signal-session-id'] as string | undefined, } ); export default function Dashboard({ data, distinctId, sessionId }) { // distinctId and sessionId are now available return
...
; } ``` ### withSignalConfig(signalConfig) Wrap your Next.js config to inject Signal env vars for the client: `NEXT_PUBLIC_SIGNAL_ENDPOINT` and `NEXT_PUBLIC_SIGNAL_PROJECT_ID`. This is an alternative to setting them in `.env.local`. | Option | Type | Description | | ------------------- | --------- | ------------------------------------------------------------------------------------------------------- | | `endpoint` | `string` | Optional. API endpoint (set as `NEXT_PUBLIC_SIGNAL_ENDPOINT`). Defaults to `https://api.trysignal.ai/`. | | `apiKey` | `string` | API key (not exposed to client by this helper) | | `projectId` | `string` | Optional. Project ID (set as `NEXT_PUBLIC_SIGNAL_PROJECT_ID`) | | `disableSourceMaps` | `boolean` | Optional. Disable source maps upload | **When to use:** When you prefer to configure Signal in `next.config.js` instead of (or in addition to) `.env.local`. ```javascript theme={null} // next.config.js const { withSignalConfig } = require('@signal-js/nextjs/server'); module.exports = withSignalConfig({ endpoint: process.env.SIGNAL_ENDPOINT, apiKey: process.env.SIGNAL_API_KEY, projectId: process.env.SIGNAL_PROJECT_ID, })({ // your existing Next.js config reactStrictMode: true, }); ``` The client will receive `process.env.NEXT_PUBLIC_SIGNAL_ENDPOINT` and `process.env.NEXT_PUBLIC_SIGNAL_PROJECT_ID` at build time. Keep `apiKey` and other secrets in server-only env (e.g. `SIGNAL_API_KEY` in `.env.local`). ## Browser options (options prop) Client `options` are the same browser SDK options as in [React — Configuration](/docs/integrations/react/configuration). That page includes: * **Transport and batching** — `compression`, `flushInterval`, `maxBatchSize`, `getSignedUploadUrl` * **Features** — `enableSessionReplay`, `enableNetworkCapture`, `enableConsoleCapture`, `enableSessionTracking`, `enablePerformanceTracking`, `enableBrowserDetection`, `debug` * **sessionRecordingMasking** — How masking works: blockClass/blockSelector (hide elements), maskTextClass/maskTextSelector (replace text with \*\*\*), ignoreClass (don’t record input values), maskAllInputs vs maskInputOptions (per-type input masking), maskTextFn/maskInputFn (custom transforms). With examples and JSX usage. * **networkCaptureOptions** — How network capture works: recordHeaders/recordBody (what is captured), sensitiveHeaders (redact header values), urlDenyList (exclude URLs), payloadSizeLimitBytes (truncate large bodies), maskRequestFn (custom redaction or drop). With examples. * **consoleCaptureOptions** — `levels`, `captureErrors`, `captureUnhandledRejections`, `stringLengthLimit`, `maskSensitiveData` * **Session and advanced** — `sessionId`, `sessionIdleTimeoutSeconds`, `maxSessionLengthSeconds`, `addTracingHeaders` (for server correlation), `rrwebConfig`, `requiredBrowserFeatures`, `debugPersistToLocalStorage`, `debugLocalStorageKey` ## See also * [Initialization](/docs/integrations/nextjs/initialization) — Client and server setup # Flush and shutdown Source: https://docs.trysignal.ai/integrations/nextjs/flush-shutdown Flush queued events and graceful shutdown (server only) # Next.js — Flush and shutdown Flush queued events immediately with `flush()`, or stop the flush timer and flush with `shutdown()` before process exit. Both are async (return a Promise). **Note:** These methods are server-side only. For client-side flushing, see [Recording control](/docs/integrations/nextjs/recording-control). ## flush() Flush all queued events to the server immediately. Returns a Promise. **When to use:** When you need to ensure events are sent before a response or before the process exits (e.g. in a serverless function or after a critical path). ```typescript theme={null} // app/api/checkout/route.ts import { signal } from '@/lib/signal-server'; import { NextRequest } from 'next/server'; export async function POST(request: NextRequest) { // ... process checkout ... // Flush events before returning response await signal.flush(); return Response.json({ success: true }); } ``` ## shutdown() Stop the flush timer and flush remaining events. Call before process exit for graceful shutdown. Returns a Promise. **When to use:** In cleanup handlers (e.g. `process.on('SIGTERM', ...)`) so no events are lost when the process exits. ```typescript theme={null} // lib/signal-server.ts import { createSignalServer } from '@signal-js/nextjs/server'; export const signal = createSignalServer({ apiKey: process.env.SIGNAL_API_KEY!, projectId: process.env.SIGNAL_PROJECT_ID!, // endpoint is optional (defaults to https://api.trysignal.ai/) }); // Graceful shutdown process.on('SIGTERM', async () => { await signal.shutdown(); process.exit(0); }); ``` ## Automatic flushing By default, the server client automatically flushes events: * When `flushBatchSize` (default: 20) events are queued * Every `flushInterval` milliseconds (default: 10000ms) Configure these in `createSignalServer` options. See [Configuration](/docs/integrations/nextjs/configuration). ## See also * [Configuration](/docs/integrations/nextjs/configuration) — flushBatchSize, flushInterval * [Recording control](/docs/integrations/nextjs/recording-control) — Client-side flush() * [Server tracking](/docs/integrations/nextjs/server-tracking) — Server-side patterns # Group Source: https://docs.trysignal.ai/integrations/nextjs/group Associate users with groups (client and server) # Next.js — Group Associate users with groups (e.g. company, organization) and set group properties. Available on both client and server. ## Client: useSignalGroup Use the same hook as the [React integration](/docs/integrations/react/group), re-exported from `@signal-js/nextjs/client`: ```tsx theme={null} 'use client'; import { useSignalGroup } from '@signal-js/nextjs/client'; function CompanySwitcher({ companies }) { const { group, getGroups } = useSignalGroup(); const handleSelectCompany = (companyId, companyName) => { group('company', companyId, { name: companyName }); }; return ( ); } ``` ## Client: group(groupType, groupKey, properties?) Associate the current user with a group. Optionally set group properties. | Parameter | Type | Required | Description | | ------------ | ------------------------- | -------- | -------------------------------------------------- | | `groupType` | `string` | yes | Type of group (e.g. `'company'`, `'organization'`) | | `groupKey` | `string` | yes | Unique identifier for the group | | `properties` | `Record` | no | Group properties to set | ## Client: getGroups() Get all groups the user is currently associated with. Returns an array of group objects. ## Server: group(distinctId, groupType, groupKey, properties?, sessionId?, windowId?) Associate a user with a group from the server. Pass `sessionId` and `windowId` for client correlation. | Parameter | Type | Required | Description | | ------------ | ------------------------- | -------- | -------------------------------------------------------- | | `distinctId` | `string` | yes | User identifier | | `groupType` | `string` | yes | Type of group (e.g. `'company'`, `'organization'`) | | `groupKey` | `string` | yes | Unique identifier for the group | | `properties` | `Record` | no | Group properties to set | | `sessionId` | `string` | no | From `x-signal-session-id` header for client correlation | | `windowId` | `string` | no | From `x-signal-window-id` header for client correlation | ```typescript theme={null} // app/api/user/company/route.ts import { signal, getSessionInfo } from '@/lib/signal-server'; import { NextRequest } from 'next/server'; export async function POST(request: NextRequest) { const { sessionId, windowId, distinctId } = getSessionInfo(request); const body = await request.json(); // Associate user with company signal.group( distinctId || body.userId, 'company', body.companyId, { role: 'admin' }, sessionId, windowId ); return Response.json({ success: true }); } ``` ## Server: groupIdentify(options, sessionId?, windowId?) Set or update group properties from the server. Pass `sessionId` and `windowId` for client correlation. | Option | Type | Required | Description | | ------------ | ------------------------- | -------- | -------------------------------------------------- | | `groupType` | `string` | yes | Type of group (e.g. `'company'`, `'organization'`) | | `groupKey` | `string` | yes | Group identifier | | `properties` | `Record` | no | Group properties to set | ```typescript theme={null} // Update company properties signal.groupIdentify( { groupType: 'company', groupKey: 'acme', properties: { plan: 'enterprise', seats: 50 } }, sessionId, windowId ); ``` ## See also * [Identify](/docs/integrations/nextjs/identify) — identify() * [Capture](/docs/integrations/nextjs/capture) — capture() * [Server tracking](/docs/integrations/nextjs/server-tracking) — Server-side patterns # Identify Source: https://docs.trysignal.ai/integrations/nextjs/identify Identify users in Next.js (client and server) # Next.js — Identify Identify users from client components (using React hooks) or from the server. On the server, pass `sessionId` and `windowId` from request headers when you have them so events stay correlated with the client session. ## Client: useSignalIdentify Use the same hook as the [React integration](/docs/integrations/react/identify), re-exported from `@signal-js/nextjs/client`: ```tsx theme={null} 'use client'; import { useSignalIdentify } from '@signal-js/nextjs/client'; export function LoginForm() { const { identify, reset } = useSignalIdentify(); const handleLogin = (user) => { identify(user.id, { email: user.email, name: user.name }); }; const handleLogout = () => { reset(); }; return ( <> ); } ``` ## Server: identify(options) From API routes or Server Actions, call `signal.identify()` with an options object. Include `sessionId` and `windowId` from the request when you want to tie the identify to the same browser session: ```typescript theme={null} import { signal, getSessionInfo } from '@/lib/signal-server'; // In an API route or Server Action after reading headers: const { sessionId, windowId, distinctId } = getSessionInfo(request); signal.identify({ distinctId: userId, properties: { email: 'user@example.com', name: 'Jane' }, sessionId, windowId, }); ``` ## See also * [Capture](/docs/integrations/nextjs/capture) — Track events * [Initialization](/docs/integrations/nextjs/initialization) — Server client setup # Initialization Source: https://docs.trysignal.ai/integrations/nextjs/initialization Install and set up Signal JS in Next.js (client and server) # Next.js — Initialization Install the Next.js integration and set up both the client provider and the server SDK so you can track events from the browser and from API routes or Server Actions. ## Installation ```bash theme={null} npm install @signal-js/nextjs ``` `@signal-js/nextjs` includes the browser SDK and the Node SDK. You do not need to install `@signal-js/browser` or `@signal-js/node` separately. ## Client setup (App Router) Create a client providers file and wrap your app with `SignalNextProvider`: ```tsx theme={null} // app/providers.tsx 'use client'; import { SignalNextProvider } from '@signal-js/nextjs/client'; export function Providers({ children }) { return ( {children} ); } ``` Wrap the root layout with your providers: ```tsx theme={null} // app/layout.tsx import { Providers } from './providers'; export default function RootLayout({ children }) { return ( {children} ); } ``` ## Server setup (App Router) Create a shared server client and a helper to read tracing headers from requests: ```typescript theme={null} // lib/signal-server.ts import { createSignalServer } from '@signal-js/nextjs/server'; export const signal = createSignalServer({ apiKey: process.env.SIGNAL_API_KEY!, projectId: process.env.SIGNAL_PROJECT_ID!, // endpoint is optional (defaults to https://api.trysignal.ai/) }); export function getSessionInfo(request: Request) { return { sessionId: request.headers.get('x-signal-session-id') || undefined, windowId: request.headers.get('x-signal-window-id') || undefined, distinctId: request.headers.get('x-signal-distinct-id') || undefined, }; } ``` Use `signal` and `getSessionInfo` in API routes, Server Actions, and (for Pages Router) in `getServerSideProps` and API route handlers. ## Environment variables Create `.env.local`: ```bash theme={null} NEXT_PUBLIC_SIGNAL_API_KEY=your-api-key NEXT_PUBLIC_SIGNAL_PROJECT_ID=your-project-id SIGNAL_API_KEY=your-api-key SIGNAL_PROJECT_ID=your-project-id ``` Use `NEXT_PUBLIC_` only for values needed in the browser. Keep server credentials without the prefix. ## Next steps * [Capture](/docs/integrations/nextjs/capture) — Client and server event tracking * [Server tracking](/docs/integrations/nextjs/server-tracking) — API routes, Server Actions, Pages Router * [Configuration](/docs/integrations/nextjs/configuration) — Provider and server options # Opt-out Source: https://docs.trysignal.ai/integrations/nextjs/opt-out Control user opt-out (client only) # Next.js — Opt-out Allow users to opt out of tracking. When opted out, no events are captured. **Note:** Opt-out is client-side only. ## Client: useSignal Get opt-out methods from `useSignal()`: ```tsx theme={null} 'use client'; import { useSignal } from '@signal-js/nextjs/client'; function PrivacyControls() { const { optOut, optIn, hasOptedOut } = useSignal(); const optedOut = hasOptedOut(); return (
{optedOut ? ( ) : ( )}
); } ``` ## optOut() Disable tracking for the current user. No events will be captured while opted out. The opt-out preference is persisted in localStorage. **When to use:** When the user requests to disable tracking (e.g. privacy settings, GDPR compliance). ```tsx theme={null} const { optOut } = useSignal(); const handleDisableTracking = () => { optOut(); alert('Tracking disabled'); }; ``` ## optIn() Re-enable tracking for the current user. Events will be captured again. **When to use:** When the user requests to re-enable tracking. ```tsx theme={null} const { optIn } = useSignal(); const handleEnableTracking = () => { optIn(); alert('Tracking enabled'); }; ``` ## hasOptedOut() Check if the user has opted out. Returns `boolean`. **When to use:** To conditionally show UI or check tracking status. ```tsx theme={null} const { hasOptedOut } = useSignal(); const isOptedOut = hasOptedOut(); if (isOptedOut) { console.log('User has opted out of tracking'); } ``` ## Example: Privacy settings ```tsx theme={null} 'use client'; import { useSignal } from '@signal-js/nextjs/client'; import { useState, useEffect } from 'react'; function PrivacySettings() { const { optOut, optIn, hasOptedOut } = useSignal(); const [optedOut, setOptedOut] = useState(false); useEffect(() => { setOptedOut(hasOptedOut()); }, [hasOptedOut]); const handleToggle = () => { if (optedOut) { optIn(); setOptedOut(false); } else { optOut(); setOptedOut(true); } }; return (

{optedOut ? 'Tracking is disabled. No events will be captured.' : 'Tracking is enabled. Events are being captured.'}

); } ``` ## Persistence The opt-out preference is stored in localStorage with the key `signal_opt_out`. It persists across page reloads and browser sessions. ## See also * [Identify](/docs/integrations/nextjs/identify) — Set user identity * [Capture](/docs/integrations/nextjs/capture) — Track events # Page views Source: https://docs.trysignal.ai/integrations/nextjs/page-views Automatic and manual page view tracking in Next.js # Next.js — Page views Next.js can track page views automatically on route changes, or you can use hooks for manual control. ## Automatic: trackPageViews With `SignalNextProvider`, set `trackPageViews={true}` (default) to send a `$pageview` on every client-side route change: ```tsx theme={null} {children} ``` No extra code is required. The provider uses the App Router’s pathname and (optionally) search params. ## Optional: trackSearchParams Set `trackSearchParams={true}` to include URL search params in page view events. This uses `useSearchParams()`, so the tracker must be inside a Suspense boundary. The provider handles this when `trackSearchParams` is true. ```tsx theme={null} {children} ``` ## Manual: usePageView and usePageViewWithSearchParams For more control, use the hooks re-exported from `@signal-js/nextjs/client`: * **usePageView(options?)** — Sends `$pageview` when the component mounts and when pathname changes. Options: `trackSearchParams`, `additionalProperties`. * **usePageViewWithSearchParams(options?)** — Same but always includes search params; must be used inside a Suspense boundary. You can use these instead of (or in addition to) automatic tracking by rendering a component that calls the hook once in your layout. For **named page views** (e.g. `$pageview_product`), use `useSignalCapture()` and call `capture('$pageview_product', { ... })` inside a `useEffect` when the route or context changes. ## See also * [Initialization](/docs/integrations/nextjs/initialization) — Provider setup * [Configuration](/docs/integrations/nextjs/configuration) — trackPageViews and trackSearchParams # Quickstart Source: https://docs.trysignal.ai/integrations/nextjs/quickstart Use Signal JS with Next.js App Router and Pages Router # Next.js Integration Use Signal JS with Next.js using the **@signal-js/nextjs** package. It provides client components (SignalNextProvider, hooks) and server utilities (createSignalServer). You add a **getSessionInfo()** helper in `lib/signal-server.ts` (see [Initialization](/docs/integrations/nextjs/initialization)) to read tracing headers for session correlation. ## Quick start 1. **Install** — `npm install @signal-js/nextjs` 2. **Client** — Wrap your app with `SignalNextProvider` in a client providers file and add it to your root layout. See [Initialization](/docs/integrations/nextjs/initialization). 3. **Server** — Create a shared `signal` client with `createSignalServer` and a `getSessionInfo()` helper in `lib/signal-server.ts`. See [Initialization](/docs/integrations/nextjs/initialization). 4. **Track events** — Use hooks on the client and `signal.capture()` on the server with session identifiers. See [Capture](/docs/integrations/nextjs/capture) and [Server tracking](/docs/integrations/nextjs/server-tracking). ## Sub-pages | Page | Description | | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- | | [Initialization](/docs/integrations/nextjs/initialization) | Install, client provider, server setup, environment variables | | [Capture](/docs/integrations/nextjs/capture) | Track events from client (hooks) and server (API routes, Server Actions) | | [Identify](/docs/integrations/nextjs/identify) | Identify users on client and server | | [Set person properties](/docs/integrations/nextjs/set-person-properties) | Update user properties with setPersonProperties, setPersonPropertiesOnce (client and server) | | [Group](/docs/integrations/nextjs/group) | Associate users with groups (client and server) | | [Super properties](/docs/integrations/nextjs/super-properties) | Register properties included with every event (client only) | | [Alias](/docs/integrations/nextjs/alias) | Link aliases to users (client and server) | | [Recording control](/docs/integrations/nextjs/recording-control) | Control recording with startRecording, stopRecording, pauseRecording, resumeRecording, flush (client only) | | [Opt-out](/docs/integrations/nextjs/opt-out) | Control user opt-out with optOut, optIn, hasOptedOut (client only) | | [Flush and shutdown](/docs/integrations/nextjs/flush-shutdown) | Flush queued events and graceful shutdown (server only) | | [Server tracking](/docs/integrations/nextjs/server-tracking) | API routes, Server Actions, Pages Router, getServerSideProps | | [Page views](/docs/integrations/nextjs/page-views) | Automatic and manual page view tracking | | [Configuration](/docs/integrations/nextjs/configuration) | SignalNextProvider and createSignalServer options | ## Features * **App Router support** — Full support for Next.js 13+ App Router * **Pages Router support** — Compatible with Pages Router * **Server-side tracking** — Track events from API routes and server actions * **Session correlation** — Link client and server events using tracing headers * **Page view tracking** — Automatic page view tracking on route changes ## Best practices 1. Use environment variables — Store credentials in `.env.local`. 2. Use addTracingHeaders only when needed — Set in `options` only if you track server-side events and want correlation. 3. Track server events — Pass `sessionId` and `windowId` from request headers to `signal.capture()` so events link to the same session replay. 4. Separate client/server configs — Use `NEXT_PUBLIC_` prefix for client-side variables. 5. Use sessionRecordingMasking — Add `signal-no-capture`, `signal-mask`, or `signal-ignore-input` classes to sensitive UI. # Recording control Source: https://docs.trysignal.ai/integrations/nextjs/recording-control Control session replay recording (client only) # Next.js — Recording control Control session replay recording and event flushing. **Note:** Recording control is client-side only. For server-side event flushing, see [Configuration](/docs/integrations/nextjs/configuration). ## Client: useSignal Get recording control methods from `useSignal()`: ```tsx theme={null} 'use client'; import { useSignal } from '@signal-js/nextjs/client'; function RecordingControls() { const { startRecording, stopRecording, pauseRecording, resumeRecording, flush, isRecording } = useSignal(); return (

Recording: {isRecording ? 'Yes' : 'No'}

); } ``` ## startRecording() Start session replay recording. Returns a Promise. **When to use:** Manually start recording if `autoStart` is `false` in `SignalNextProvider`, or restart recording after stopping. ```tsx theme={null} const { startRecording } = useSignal(); const handleStart = async () => { await startRecording(); console.log('Recording started'); }; ``` ## stopRecording() Stop session replay recording. Stops rrweb recorder, network capture, and console capture. **When to use:** Stop recording when the user navigates away or when you want to disable tracking temporarily. ```tsx theme={null} const { stopRecording } = useSignal(); const handleStop = () => { stopRecording(); console.log('Recording stopped'); }; ``` ## pauseRecording() Pause recording temporarily. Events are not captured while paused. **When to use:** Pause recording for sensitive operations (e.g. payment forms, password entry) without fully stopping. ```tsx theme={null} const { pauseRecording } = useSignal(); const handleSensitiveAction = () => { pauseRecording(); // ... perform sensitive operation }; ``` ## resumeRecording() Resume recording after pausing. **When to use:** Resume recording after a sensitive operation is complete. ```tsx theme={null} const { resumeRecording } = useSignal(); const handleSensitiveActionComplete = () => { resumeRecording(); // ... continue normal operation }; ``` ## flush() Flush all queued events to the server immediately. Returns a Promise. **When to use:** Before page unload, before navigation, or when you need to ensure events are sent immediately. ```tsx theme={null} const { flush } = useSignal(); useEffect(() => { const handleBeforeUnload = async () => { await flush(); }; window.addEventListener('beforeunload', handleBeforeUnload); return () => window.removeEventListener('beforeunload', handleBeforeUnload); }, [flush]); ``` ## Example: Conditional recording ```tsx theme={null} 'use client'; import { useSignal } from '@signal-js/nextjs/client'; import { useEffect } from 'react'; function ConditionalRecording({ userConsent }) { const { startRecording, stopRecording, isRecording } = useSignal(); useEffect(() => { if (userConsent) { startRecording(); } else { stopRecording(); } }, [userConsent, startRecording, stopRecording]); return (
{isRecording ? 'Recording active' : 'Recording paused'}
); } ``` ## Server-side flushing For server-side event flushing, use `signal.flush()` from your server client. See [Configuration](/docs/integrations/nextjs/configuration) for `createSignalServer` options including `flushBatchSize` and `flushInterval`. ## See also * [Initialization](/docs/integrations/nextjs/initialization) — SignalNextProvider and autoStart * [Capture](/docs/integrations/nextjs/capture) — Track events * [Configuration](/docs/integrations/nextjs/configuration) — Server-side flush options # Server tracking Source: https://docs.trysignal.ai/integrations/nextjs/server-tracking Track events from API routes, Server Actions, and Pages Router # Next.js — Server tracking Track events from the server so they appear in the same session as client events. Use your shared `signal` client and pass `sessionId` and `windowId` from request headers into every `signal.capture()` or `signal.identify()` call. ## App Router: API routes Use `getSessionInfo(request)` with the route’s `Request`: ```typescript theme={null} // app/api/checkout/route.ts import { signal, getSessionInfo } from '@/lib/signal-server'; import { NextRequest } from 'next/server'; export async function POST(request: NextRequest) { const { sessionId, windowId, distinctId } = getSessionInfo(request); const body = await request.json(); const orderId = await processOrder(body); signal.capture({ distinctId: distinctId || body.userId, event: 'order_processed', properties: { orderId, amount: body.amount }, sessionId, windowId, }); return Response.json({ success: true, orderId }); } ``` ## App Router: Server Actions Read headers with `headers()` from `next/headers` and pass the same identifiers: ```typescript theme={null} // app/actions.ts 'use server'; import { signal } from '@/lib/signal-server'; import { headers } from 'next/headers'; export async function processOrderAction(orderData: OrderData) { const headersList = headers(); const sessionId = headersList.get('x-signal-session-id') || undefined; const windowId = headersList.get('x-signal-window-id') || undefined; const distinctId = headersList.get('x-signal-distinct-id') || undefined; const orderId = await processOrder(orderData); signal.capture({ distinctId: distinctId || orderData.userId, event: 'order_processed', properties: { orderId, amount: orderData.amount }, sessionId, windowId, }); return { success: true, orderId }; } ``` ## Pages Router: API routes Use `req.headers` and a small helper: ```typescript theme={null} // pages/api/orders.ts import { NextApiRequest, NextApiResponse } from 'next'; import { signal } from '@/lib/signal-server'; function getSessionInfo(req: NextApiRequest) { return { sessionId: req.headers['x-signal-session-id'] as string | undefined, windowId: req.headers['x-signal-window-id'] as string | undefined, distinctId: req.headers['x-signal-distinct-id'] as string | undefined, }; } export default async function handler(req: NextApiRequest, res: NextApiResponse) { const { sessionId, windowId, distinctId } = getSessionInfo(req); const orderId = await processOrder(req.body); signal.capture({ distinctId: distinctId || req.body.userId, event: 'order_created', properties: { orderId }, sessionId, windowId, }); res.json({ success: true, orderId }); } ``` ## Pages Router: getServerSideProps Read headers from `context.req` and pass them into `signal.capture()`: ```typescript theme={null} // pages/dashboard.tsx import { GetServerSideProps } from 'next'; import { signal } from '@/lib/signal-server'; export const getServerSideProps: GetServerSideProps = async (context) => { const sessionId = context.req.headers['x-signal-session-id'] as string | undefined; const windowId = context.req.headers['x-signal-window-id'] as string | undefined; const distinctId = context.req.headers['x-signal-distinct-id'] as string | undefined; signal.capture({ distinctId: distinctId || 'anonymous', event: '$pageview', properties: { path: context.resolvedUrl }, sessionId, windowId, }); return { props: {} }; }; ``` ## Enabling tracing headers on the client For the server to receive `x-signal-session-id`, `x-signal-window-id`, and `x-signal-distinct-id`, the client must send them. Set `addTracingHeaders: true` (or an array of hostnames) in your `SignalNextProvider` `options` when you use server-side tracking. See [Configuration](/docs/integrations/nextjs/configuration). ## Advanced server helpers The server package also exports: * **trackServerEvent(options)** — Convenience that uses the singleton from `createSignalServer` to capture one event (e.g. in a one-off handler). Options: `distinctId`, `event`, `properties`. * **getSignalServerSideProps(getServerSidePropsFunc, options)** — Wraps your `getServerSideProps` to inject Signal props (e.g. `distinctId`, `sessionId`) from `getDistinctId` / `getSessionId` callbacks. Use when you want Signal context in page props. * **withSignalConfig(nextConfig)** — Wraps your Next.js config to inject Signal env vars (`NEXT_PUBLIC_SIGNAL_ENDPOINT`, `NEXT_PUBLIC_SIGNAL_PROJECT_ID`). Use if you prefer config over `.env.local`. ## See also * [Capture](/docs/integrations/nextjs/capture) — capture() options * [Initialization](/docs/integrations/nextjs/initialization) — lib/signal-server setup # Set person properties Source: https://docs.trysignal.ai/integrations/nextjs/set-person-properties Update user properties with setPersonProperties and setPersonPropertiesOnce (client and server) # Next.js — Set person properties Update user properties without changing the distinct ID. Use `setPersonProperties` to overwrite properties or `setPersonPropertiesOnce` to set them only if not already set. Available on both client and server. ## Client: useSignal Get `setPersonProperties` and `setPersonPropertiesOnce` from `useSignal()`: ```tsx theme={null} 'use client'; import { useSignal } from '@signal-js/nextjs/client'; function UserProfile({ user }) { const { setPersonProperties, setPersonPropertiesOnce } = useSignal(); const handlePlanUpgrade = () => { setPersonProperties({ plan: 'pro', role: 'admin' }); }; const handleSignup = () => { // Only set if not already set setPersonPropertiesOnce({ signupSource: 'organic' }); }; return (
); } ``` ## Client: setPersonProperties(properties) Sets or updates user properties (overwrites existing). Sends a `$set` event. | Parameter | Type | Required | Description | | ------------ | ------------------------- | -------- | -------------------------------- | | `properties` | `Record` | yes | User properties to set or update | **When to use:** Update user properties after initial identification (e.g. plan changes, role updates). ## Client: setPersonPropertiesOnce(properties) Sets user properties only if they are not already set. Sends a `$set_once` event. | Parameter | Type | Required | Description | | ------------ | ------------------------- | -------- | ------------------------------------------------ | | `properties` | `Record` | yes | User properties to set (only if not already set) | **When to use:** One-time attributes that shouldn't be overwritten (e.g. `signupSource`, `firstPurchaseDate`). ## Server: setPersonProperties(distinctId, properties, sessionId?, windowId?) Sets or updates user properties from the server. Pass `sessionId` and `windowId` for client correlation. | Parameter | Type | Required | Description | | ------------ | ------------------------- | -------- | -------------------------------------------------------- | | `distinctId` | `string` | yes | User identifier | | `properties` | `Record` | yes | User properties to set or update | | `sessionId` | `string` | no | From `x-signal-session-id` header for client correlation | | `windowId` | `string` | no | From `x-signal-window-id` header for client correlation | ```typescript theme={null} // app/api/user/route.ts import { signal, getSessionInfo } from '@/lib/signal-server'; import { NextRequest } from 'next/server'; export async function PATCH(request: NextRequest) { const { sessionId, windowId, distinctId } = getSessionInfo(request); const body = await request.json(); // Update user plan signal.setPersonProperties( distinctId || body.userId, { plan: 'enterprise', seats: 50 }, sessionId, windowId ); return Response.json({ success: true }); } ``` ## Server: setPersonPropertiesOnce(distinctId, properties, sessionId?, windowId?) Sets user properties only if not already set. Pass `sessionId` and `windowId` for client correlation. | Parameter | Type | Required | Description | | ------------ | ------------------------- | -------- | ------------------------------------------------ | | `distinctId` | `string` | yes | User identifier | | `properties` | `Record` | yes | User properties to set (only if not already set) | | `sessionId` | `string` | no | From header for client correlation | | `windowId` | `string` | no | From header for client correlation | ```typescript theme={null} // Set signup source only if not already set signal.setPersonPropertiesOnce( userId, { signupSource: 'organic' }, sessionId, windowId ); ``` ## Differences from identify() * **identify(distinctId, traits)** — Changes the user's distinct ID and sets properties. Use after login. * **setPersonProperties(properties)** — Updates properties without changing the distinct ID. Use when properties change after identification. * **setPersonPropertiesOnce(properties)** — Sets properties only if not already set. Use for immutable attributes. ## See also * [Identify](/docs/integrations/nextjs/identify) — identify() and reset() * [Capture](/docs/integrations/nextjs/capture) — Track events * [Server tracking](/docs/integrations/nextjs/server-tracking) — Server-side patterns # Super properties Source: https://docs.trysignal.ai/integrations/nextjs/super-properties Register properties included with every event (client only) # Next.js — Super properties Super properties are automatically included with every event you capture. Use `register()` to add properties, `registerOnce()` to add them only if not already set, and `unregister()` to remove them. **Note:** Super properties are client-side only; they don't apply to server-side events. ## Client: useSignal Get `register`, `registerOnce`, `unregister`, and `getSuperProperties` from `useSignal()`: ```tsx theme={null} 'use client'; import { useSignal } from '@signal-js/nextjs/client'; import { useEffect } from 'react'; function App() { const { register, registerOnce, unregister, getSuperProperties } = useSignal(); useEffect(() => { // Register app version with all events register({ appVersion: '1.2.3' }); // Register environment only once registerOnce({ environment: 'production' }); }, []); return ; } ``` ## register(properties) Add properties that will be included with every event. Overwrites existing properties with the same keys. | Parameter | Type | Required | Description | | ------------ | ------------------------- | -------- | ------------------------------------- | | `properties` | `Record` | yes | Properties to include with all events | **When to use:** Properties that apply to all events (e.g. app version, environment, user role, A/B test variants). ```tsx theme={null} const { register } = useSignal(); // Register app version - will be included in all events register({ appVersion: '1.2.3', environment: 'production' }); // Later, update app version (overwrites previous value) register({ appVersion: '1.2.4' }); ``` ## registerOnce(properties) Add properties only if they are not already registered. Useful for one-time attributes. | Parameter | Type | Required | Description | | ------------ | ------------------------- | -------- | ------------------------------------------------ | | `properties` | `Record` | yes | Properties to register (only if not already set) | **When to use:** Properties that should be set once and never overwritten (e.g. initial app version, first session date). ```tsx theme={null} const { registerOnce } = useSignal(); // Register initial app version only once registerOnce({ initialAppVersion: '1.0.0' }); ``` ## unregister(propertyName) Remove a super property so it's no longer included with events. | Parameter | Type | Required | Description | | -------------- | -------- | -------- | ------------------------------ | | `propertyName` | `string` | yes | Name of the property to remove | **When to use:** When a property no longer applies (e.g. user leaves an A/B test, app version is deprecated). ```tsx theme={null} const { unregister } = useSignal(); // Remove app version from super properties unregister('appVersion'); ``` ## getSuperProperties() Get all currently registered super properties. Returns `Record`. **When to use:** Debugging or checking which properties are registered. ```tsx theme={null} const { getSuperProperties } = useSignal(); const superProps = getSuperProperties(); console.log('Super properties:', superProps); ``` ## Example: A/B testing ```tsx theme={null} 'use client'; import { useSignal } from '@signal-js/nextjs/client'; import { useEffect } from 'react'; function ExperimentBanner({ experimentVariant }) { const { register, unregister } = useSignal(); useEffect(() => { // Register experiment variant with all events register({ experimentVariant }); return () => { // Clean up when component unmounts unregister('experimentVariant'); }; }, [experimentVariant, register, unregister]); return
Experiment: {experimentVariant}
; } ``` ## See also * [Capture](/docs/integrations/nextjs/capture) — Track events (super properties are included automatically) * [Identify](/docs/integrations/nextjs/identify) — Set user identity # Alias Source: https://docs.trysignal.ai/integrations/node/alias Link an alias to a user with signal.alias() # Node — Alias Link an alias (e.g. anonymous ID) to a user with `signal.alias()`. Use when merging anonymous and identified users (e.g. after login). Sends `$create_alias`. ## alias(distinctId, alias, sessionId?, windowId?) Link an alias (e.g. anonymous ID) to a user. Sends `$create_alias` event. | Parameter | Type | Required | Description | | ------------ | -------- | -------- | -------------------------------------------------------- | | `distinctId` | `string` | yes | Current user identifier (e.g. after login) | | `alias` | `string` | yes | Alias to link (e.g. previous anonymous ID) | | `sessionId` | `string` | no | From `x-signal-session-id` header for client correlation | | `windowId` | `string` | no | From `x-signal-window-id` header for client correlation | **When to use:** After login to merge anonymous events (from before login) with the identified user. This ensures all events are associated with the same user profile. ```typescript theme={null} const { sessionId, windowId, distinctId } = getSessionInfo(req); signal.alias( distinctId || userId, anonymousId, sessionId, windowId ); ``` ## Example: Login flow ```typescript theme={null} // After user logs in const { sessionId, windowId, distinctId } = getSessionInfo(req); // Identify the user signal.identify({ distinctId: userId, properties: { email: user.email, name: user.name }, sessionId, windowId, }); // Link anonymous ID if it exists if (anonymousId) { signal.alias(userId, anonymousId, sessionId, windowId); } ``` ## See also * [Identify](/docs/integrations/node/identify) — identify() * [Capture](/docs/integrations/node/capture) — capture() # Capture Source: https://docs.trysignal.ai/integrations/node/capture Track events with signal.capture() # Node — Capture Track events from the server with `signal.capture(options)`. Pass `sessionId` and `windowId` from request headers to correlate with the client session. ## capture(options) | Option | Type | Required | Description | | ------------ | -------------------------- | -------- | -------------------------------------------------------- | | `distinctId` | `string` | yes | User identifier | | `event` | `string` | yes | Event name | | `properties` | `Record` | no | Event properties | | `timestamp` | `Date` | no | When the event occurred (default: now) | | `groups` | `Record` | no | Groups the user belongs to (e.g. `{ company: 'acme' }`) | | `sessionId` | `string` | no | From `x-signal-session-id` header for client correlation | | `windowId` | `string` | no | From `x-signal-window-id` header for client correlation | ## Example ```typescript theme={null} const { sessionId, windowId, distinctId } = getSessionInfo(req); signal.capture({ distinctId: distinctId || userId, event: 'order_created', properties: { orderId, amount }, sessionId, windowId, }); ``` Events are queued and sent in batches. All methods are synchronous except [flush](/docs/integrations/node/flush-shutdown) and [shutdown](/docs/integrations/node/flush-shutdown). ## See also * [Identify](/docs/integrations/node/identify) — Identify users * [Initialization](/docs/integrations/node/initialization) — getSessionInfo pattern # Configuration Source: https://docs.trysignal.ai/integrations/node/configuration createSignalNode options for Node # Node — Configuration Configure the Node client with `createSignalNode(options)`. Required: `apiKey`. Optional: `endpoint` (defaults to `https://api.trysignal.ai/`). ## createSignalNode options | Option | Type | Default | Description | | ---------------- | -------------- | --------------------------- | ----------------------------------------- | | `endpoint` | `string` | `https://api.trysignal.ai/` | API endpoint for sending events | | `apiKey` | `string` | required | API key for authentication | | `projectId` | `string` | `'default'` | Project ID | | `debug` | `boolean` | `false` | Enable debug logging | | `flushBatchSize` | `number` | `20` | Flush when this many events are queued | | `flushInterval` | `number` | `10000` | Flush interval in ms | | `timeout` | `number` | `30000` | Request timeout in ms | | `compression` | `boolean` | `true` | Enable gzip compression | | `personalApiKey` | `string` | `''` | Personal API key (e.g. for feature flags) | | `fetch` | `typeof fetch` | `globalThis.fetch` | Custom fetch implementation | ## Available methods After creating a client with `createSignalNode()`, you can call these methods on the instance. All methods are synchronous (return immediately) except `flush()` and `shutdown()` which are async. ### capture(options) Track custom events from the server. Pass `sessionId` and `windowId` from request headers to correlate with client sessions. | Option | Type | Required | Description | | ------------ | -------------------------- | -------- | -------------------------------------------------------- | | `distinctId` | `string` | yes | User identifier | | `event` | `string` | yes | Event name | | `properties` | `Record` | no | Event properties | | `timestamp` | `Date` | no | When the event occurred (default: now) | | `groups` | `Record` | no | Groups the user belongs to (e.g. `{ company: 'acme' }`) | | `sessionId` | `string` | no | From `x-signal-session-id` header for client correlation | | `windowId` | `string` | no | From `x-signal-window-id` header for client correlation | **When to use:** Track any business event (e.g. `order_created`, `payment_processed`, `user_action`). Events are queued and sent in batches. ```typescript theme={null} const { sessionId, windowId, distinctId } = getSessionInfo(req); signal.capture({ distinctId: distinctId || userId, event: 'order_created', properties: { orderId, amount }, sessionId, windowId, }); ``` See [Capture](/docs/integrations/node/capture) for details. ### identify(options) Identify a user and set their properties. Sends an `$identify` event. | Option | Type | Required | Description | | ------------ | ------------ | -------- | ----------------------------------------------------- | | `distinctId` | `string` | yes | User identifier | | `properties` | `UserTraits` | no | User properties to set (e.g. `email`, `name`, `plan`) | | `sessionId` | `string` | no | From header for client correlation | | `windowId` | `string` | no | From header for client correlation | **When to use:** After login or when you know the user's identity. Subsequent events will be associated with this user. ```typescript theme={null} signal.identify({ distinctId: 'user_123', properties: { email: 'user@example.com', name: 'Jane' }, sessionId, windowId, }); ``` See [Identify](/docs/integrations/node/identify) for details. ### setPersonProperties(distinctId, properties, sessionId?, windowId?) Set or update user properties (overwrites existing). Sends `$set` event. **When to use:** Update user properties without sending an `$identify` event (e.g. when properties change after initial identification). ```typescript theme={null} signal.setPersonProperties('user_123', { plan: 'pro', role: 'admin' }, sessionId, windowId); ``` ### setPersonPropertiesOnce(distinctId, properties, sessionId?, windowId?) Set user properties only if not already set. Sends `$set_once` event. **When to use:** One-time attributes that shouldn't be overwritten (e.g. `signupSource`, `firstPurchaseDate`). ```typescript theme={null} signal.setPersonPropertiesOnce('user_123', { signupSource: 'organic' }, sessionId, windowId); ``` See [Set person properties](/docs/integrations/node/set-person-properties) for details. ### group(distinctId, groupType, groupKey, properties?, sessionId?, windowId?) Associate a user with a group (e.g. company, organization). Sends `$group_assign` event. If `properties` are provided, also calls `groupIdentify()` to set group properties. **When to use:** When a user joins or is associated with a group (e.g. company, team, organization). ```typescript theme={null} signal.group('user_123', 'company', 'acme', { role: 'admin' }, sessionId, windowId); ``` ### groupIdentify(options, sessionId?, windowId?) Set or update group properties. Sends `$groupidentify` event. | Option | Type | Required | Description | | ------------ | -------------------------- | -------- | ---------------------------------- | | `groupType` | `string` | yes | e.g. `'company'`, `'organization'` | | `groupKey` | `string` | yes | Group identifier | | `properties` | `Record` | no | Group properties to set | **When to use:** Update group-level attributes (e.g. company plan, team size) without associating a user. ```typescript theme={null} signal.groupIdentify( { groupType: 'company', groupKey: 'acme', properties: { plan: 'enterprise', seats: 50 } }, sessionId, windowId ); ``` See [Group](/docs/integrations/node/group) for details. ### alias(distinctId, alias, sessionId?, windowId?) Link an alias (e.g. anonymous ID) to a user. Sends `$create_alias` event. **When to use:** After login to merge anonymous events (from before login) with the identified user. ```typescript theme={null} signal.alias('user_123', 'anonymous_abc', sessionId, windowId); ``` See [Alias](/docs/integrations/node/alias) for details. ### flush() Flush all queued events to the server immediately. Returns a Promise. **When to use:** Before a response in serverless functions, after critical paths, or when you need to ensure events are sent immediately. ```typescript theme={null} await signal.flush(); ``` ### shutdown() Stop the flush timer and flush remaining events. Returns a Promise. Call before process exit for graceful shutdown. **When to use:** In cleanup handlers (e.g. `process.on('SIGTERM', ...)`) to ensure no events are lost when the process exits. ```typescript theme={null} await signal.shutdown(); ``` See [Flush and shutdown](/docs/integrations/node/flush-shutdown) for details. ## Client-side: tracing headers For session correlation, the **client** must send tracing headers. Use **@signal-js/browser** (or React/Next.js/Vue) and set `addTracingHeaders: true` (or an array of hostnames). See [Vue](/docs/integrations/vue/configuration) or [React](/docs/integrations/react/configuration) for client options. ## See also * [Initialization](/docs/integrations/node/initialization) — Setup * [Flush and shutdown](/docs/integrations/node/flush-shutdown) — flush(), shutdown() # Flush and shutdown Source: https://docs.trysignal.ai/integrations/node/flush-shutdown Flush queued events and graceful shutdown # Node — Flush and shutdown Flush queued events immediately with `flush()`, or stop the flush timer and flush with `shutdown()` before process exit. Both are async (return a Promise). ## flush() Flush all queued events to the server immediately. ```typescript theme={null} await signal.flush(); ``` Use when you need to ensure events are sent before a response or before the process exits (e.g. in a serverless function or after a critical path). ## shutdown() Stop the flush timer and flush remaining events. Call before process exit for graceful shutdown. ```typescript theme={null} await signal.shutdown(); ``` Use in cleanup handlers (e.g. `process.on('SIGTERM', ...)`) so no events are lost when the process exits. ## See also * [Initialization](/docs/integrations/node/initialization) — One client per process * [Configuration](/docs/integrations/node/configuration) — flushBatchSize, flushInterval # Group Source: https://docs.trysignal.ai/integrations/node/group Associate users with groups and set group properties # Node — Group Associate users with groups (e.g. company, organization) and set group properties. Use `groupIdentify()` to set or update group attributes and `group()` to associate a user with a group. ## groupIdentify(options, sessionId?, windowId?) Set or update group properties. Sends `$groupidentify`. | Option | Type | Required | Description | | ------------ | -------------------------- | -------- | ---------------------------------- | | `groupType` | `string` | yes | e.g. `'company'`, `'organization'` | | `groupKey` | `string` | yes | Group identifier | | `properties` | `Record` | no | Group properties to set | ```typescript theme={null} signal.groupIdentify( { groupType: 'company', groupKey: 'acme', properties: { plan: 'enterprise', seats: 50 } }, sessionId, windowId ); ``` ## group(distinctId, groupType, groupKey, properties?, sessionId?, windowId?) Associate a user with a group. Sends `$group_assign` event. If `properties` are provided, also calls `groupIdentify()` to set group properties. | Parameter | Type | Required | Description | | ------------ | -------------------------- | -------- | -------------------------------------------------------- | | `distinctId` | `string` | yes | User identifier | | `groupType` | `string` | yes | Type of group (e.g. `'company'`, `'organization'`) | | `groupKey` | `string` | yes | Unique identifier for the group | | `properties` | `Record` | no | Group properties to set | | `sessionId` | `string` | no | From `x-signal-session-id` header for client correlation | | `windowId` | `string` | no | From `x-signal-window-id` header for client correlation | **When to use:** When a user joins or is associated with a group (e.g. company, team, organization). ```typescript theme={null} const { sessionId, windowId, distinctId } = getSessionInfo(req); signal.group( distinctId || userId, 'company', 'acme', { role: 'admin' }, sessionId, windowId ); ``` ## See also * [Identify](/docs/integrations/node/identify) — identify() * [Capture](/docs/integrations/node/capture) — capture() # Identify Source: https://docs.trysignal.ai/integrations/node/identify Identify users with signal.identify() # Node — Identify Identify a user and set their properties with `signal.identify(options)`. Sends an `$identify` event. Pass `sessionId` and `windowId` from request headers when you want to correlate with the client session. ## identify(options) | Option | Type | Required | Description | | ------------ | ------------ | -------- | ---------------------------------- | | `distinctId` | `string` | yes | User identifier | | `properties` | `UserTraits` | no | User properties to set | | `sessionId` | `string` | no | From header for client correlation | | `windowId` | `string` | no | From header for client correlation | ## Example ```typescript theme={null} const { sessionId, windowId, distinctId } = getSessionInfo(req); signal.identify({ distinctId: 'user_123', properties: { email: 'user@example.com', name: 'Jane' }, sessionId, windowId, }); ``` ## See also * [Set person properties](/docs/integrations/node/set-person-properties) — setPersonProperties, setPersonPropertiesOnce * [Capture](/docs/integrations/node/capture) — Track events # Initialization Source: https://docs.trysignal.ai/integrations/node/initialization Install and set up @signal-js/node with Node.js or Express # Node — Initialization Install **@signal-js/node** and create one client with `createSignalNode()`. Use it in plain Node.js `http`/`https` servers or in Express. Reuse the same client across all requests. ## Installation ```bash theme={null} npm install @signal-js/node ``` ## Using with Node.js Create the client at startup and a helper to read tracing headers from `http.IncomingMessage`: ```typescript theme={null} import http from 'http'; import { createSignalNode } from '@signal-js/node'; const signal = createSignalNode({ apiKey: process.env.SIGNAL_API_KEY!, projectId: process.env.SIGNAL_PROJECT_ID!, // endpoint is optional (defaults to https://api.trysignal.ai/) }); function getSessionInfo(req: http.IncomingMessage) { const headers = req.headers; return { sessionId: headers['x-signal-session-id'] as string | undefined, windowId: headers['x-signal-window-id'] as string | undefined, distinctId: headers['x-signal-distinct-id'] as string | undefined, }; } const server = http.createServer(async (req, res) => { const { sessionId, windowId, distinctId } = getSessionInfo(req); // Use signal.capture(), signal.identify(), etc. with sessionId, windowId, distinctId }); server.listen(3000); ``` ## Using with Express Same client; read headers from `req.headers`: ```typescript theme={null} import express from 'express'; import { createSignalNode } from '@signal-js/node'; const app = express(); app.use(express.json()); const signal = createSignalNode({ apiKey: process.env.SIGNAL_API_KEY!, projectId: process.env.SIGNAL_PROJECT_ID!, // endpoint is optional (defaults to https://api.trysignal.ai/) }); function getSessionInfo(req: express.Request) { return { sessionId: req.headers['x-signal-session-id'] as string | undefined, windowId: req.headers['x-signal-window-id'] as string | undefined, distinctId: req.headers['x-signal-distinct-id'] as string | undefined, }; } // Use signal and getSessionInfo in routes ``` Create one `createSignalNode()` instance per process and reuse it. For session correlation, the client must send tracing headers; see [Vue](/docs/integrations/vue/configuration) or [React](/docs/integrations/react/configuration) for `addTracingHeaders`. ## Next steps * [Capture](/docs/integrations/node/capture) — Track events * [Identify](/docs/integrations/node/identify) — Identify users * [Configuration](/docs/integrations/node/configuration) — createSignalNode options # Quickstart Source: https://docs.trysignal.ai/integrations/node/quickstart Use @signal-js/node with Node.js or Express to track server events and correlate with client sessions # Node Integration Use **@signal-js/node** with **Node.js** or **Express** to track events on the server and correlate them with client-side sessions. Create one client with `createSignalNode()` and reuse it across requests. Pass `sessionId` and `windowId` from request headers to link server events to the same session replay. ## Quick start 1. **Install** — `npm install @signal-js/node` 2. **Initialize** — Create a client with `createSignalNode({ endpoint, apiKey, projectId })` and a helper to read `x-signal-session-id`, `x-signal-window-id`, `x-signal-distinct-id` from requests. See [Initialization](/docs/integrations/node/initialization). 3. **Track events** — Call `signal.capture()` with `sessionId` and `windowId` from the request. See [Capture](/docs/integrations/node/capture). ## Sub-pages | Page | Description | | ---------------------------------------------------------------------- | ----------------------------------------------------- | | [Initialization](/docs/integrations/node/initialization) | Install, Node.js setup, Express setup, getSessionInfo | | [Capture](/docs/integrations/node/capture) | Track events with signal.capture() | | [Identify](/docs/integrations/node/identify) | Identify users with signal.identify() | | [Set person properties](/docs/integrations/node/set-person-properties) | setPersonProperties, setPersonPropertiesOnce | | [Group](/docs/integrations/node/group) | groupIdentify, group — associate users with groups | | [Alias](/docs/integrations/node/alias) | Link alias to user with signal.alias() | | [Flush and shutdown](/docs/integrations/node/flush-shutdown) | flush(), shutdown() | | [Configuration](/docs/integrations/node/configuration) | createSignalNode options | ## How it works 1. **Client SDK** generates `sessionId` and `windowId` when initialized. 2. **Tracing headers** (when `addTracingHeaders` is enabled on the client) add these to fetch/XHR: `X-Signal-Session-Id`, `X-Signal-Window-Id`, `X-Signal-Distinct-Id`. 3. **Server** reads these headers and passes `sessionId` and `windowId` into `signal.capture()` (and other methods). 4. **Result** — Client and server events share the same identifiers and appear together in session replay. ## Best practices 1. Extract headers — Read the tracing headers from every request when you want correlation. 2. Fallback to user ID — Use `distinctId` from headers when present, otherwise your own user ID. 3. Track key events — Call `signal.capture()` for important business events. 4. One client per process — Create a single `createSignalNode()` instance and reuse it. 5. Test correlation — Confirm client and server events appear in the same session in the dashboard. # Set person properties Source: https://docs.trysignal.ai/integrations/node/set-person-properties Set user properties with setPersonProperties and setPersonPropertiesOnce # Node — Set person properties Set or update user properties from the server with `setPersonProperties` (overwrites) or `setPersonPropertiesOnce` (only if not already set). Pass `sessionId` and `windowId` when correlating with the client session. ## setPersonProperties(distinctId, properties, sessionId?, windowId?) Sets user properties (overwrites). Sends `$set` event. | Parameter | Type | Required | Description | | ------------ | ------------ | -------- | -------------------------------------------------------- | | `distinctId` | `string` | yes | User identifier | | `properties` | `UserTraits` | yes | User properties to set or update | | `sessionId` | `string` | no | From `x-signal-session-id` header for client correlation | | `windowId` | `string` | no | From `x-signal-window-id` header for client correlation | **When to use:** Update user properties without sending an `$identify` event (e.g. when properties change after initial identification). ```typescript theme={null} const { sessionId, windowId, distinctId } = getSessionInfo(req); signal.setPersonProperties( distinctId || userId, { plan: 'pro', role: 'admin' }, sessionId, windowId ); ``` ## setPersonPropertiesOnce(distinctId, properties, sessionId?, windowId?) Sets user properties only if not already set. Sends `$set_once` event. | Parameter | Type | Required | Description | | ------------ | ------------ | -------- | -------------------------------------------------------- | | `distinctId` | `string` | yes | User identifier | | `properties` | `UserTraits` | yes | User properties to set (only if not already set) | | `sessionId` | `string` | no | From `x-signal-session-id` header for client correlation | | `windowId` | `string` | no | From `x-signal-window-id` header for client correlation | **When to use:** One-time attributes that shouldn't be overwritten (e.g. `signupSource`, `firstPurchaseDate`). ```typescript theme={null} const { sessionId, windowId, distinctId } = getSessionInfo(req); signal.setPersonPropertiesOnce( distinctId || userId, { signupSource: 'organic' }, sessionId, windowId ); ``` ## See also * [Identify](/docs/integrations/node/identify) — identify() * [Capture](/docs/integrations/node/capture) — capture() # Alias Source: https://docs.trysignal.ai/integrations/react/alias Link an alias to a user with signal.alias() # React — Alias Link an alias (e.g. anonymous ID) to the current user. Use this to merge anonymous events (from before login) with the identified user. Access via `useSignal()`. ## Accessing the method Get `alias` from `useSignal()`: ```tsx theme={null} import { useSignal } from '@signal-js/react'; function LoginHandler({ anonymousId, userId }) { const { alias } = useSignal(); const handleLogin = () => { // Link anonymous ID to the identified user if (anonymousId) { alias(anonymousId); } }; return ; } ``` ## alias(alias) Links an alias to the current user. Sends a `$create_alias` event. | Parameter | Type | Required | Description | | --------- | -------- | -------- | ------------------------------------------ | | `alias` | `string` | yes | Alias to link (e.g. previous anonymous ID) | **When to use:** After login to merge anonymous events (from before login) with the identified user. This ensures all events are associated with the same user profile. ## Example: Login flow ```tsx theme={null} import { useSignal } from '@signal-js/react'; import { useEffect, useState } from 'react'; function LoginFlow() { const { identify, alias, distinctId } = useSignal(); const [anonymousId, setAnonymousId] = useState(null); useEffect(() => { // Store anonymous ID before login if (!distinctId) { // Get anonymous ID from localStorage or generate one const stored = localStorage.getItem('anonymous_id'); if (stored) { setAnonymousId(stored); } } }, [distinctId]); const handleLogin = async (userId: string, userTraits: Record) => { // Identify the user identify(userId, userTraits); // Link anonymous ID if it exists if (anonymousId) { alias(anonymousId); localStorage.removeItem('anonymous_id'); } }; return ( ); } ``` ## How it works 1. **Before login:** User browses anonymously. Events are tracked with an anonymous ID. 2. **After login:** Call `identify(userId, traits)` to set the user's identity. 3. **Link alias:** Call `alias(anonymousId)` to link the anonymous ID to the identified user. 4. **Result:** All events (both anonymous and identified) are now associated with the same user profile. ## See also * [Identify](/docs/integrations/react/identify) — identify() and reset() * [Capture](/docs/integrations/react/capture) — Track events # Capture Source: https://docs.trysignal.ai/integrations/react/capture Track custom events in React with useSignal or useSignalCapture # React — Capture Track custom events from your React components using the Signal hooks. Use `useSignal()` for full SDK access or `useSignalCapture()` for a memoized capture function. ## useSignal Get the full Signal SDK instance and call `capture` (or any other method): ```tsx theme={null} import { useSignal } from '@signal-js/react'; function MyComponent() { const signal = useSignal(); const handleClick = () => { signal.capture('button_clicked', { buttonId: 'cta' }); }; return ; } ``` `useSignal()` returns: `signal`, `isInitialized`, `isRecording`, `sessionId`, `distinctId`, `capture`, `identify`, `setPersonProperties`, `setPersonPropertiesOnce`, `group`, `register`, `registerOnce`, `unregister`, `getSuperProperties`, `alias`, `reset`, `optOut`, `optIn`, `hasOptedOut`, `startRecording`, `stopRecording`, `pauseRecording`, `resumeRecording`, `flush`. See individual method pages for details. ## useSignalCapture Get only a memoized capture function (useful when you only need to track events): ```tsx theme={null} import { useSignalCapture } from '@signal-js/react'; function MyComponent() { const capture = useSignalCapture(); const handleClick = () => { capture('button_clicked', { buttonId: 'cta' }); }; return ; } ``` ## capture(eventName, properties?) * **eventName** (`string`) — Event name (e.g. `'order_created'`, `'add_to_cart'`). * **properties** (`Record` optional) — Arbitrary JSON-serializable properties. Both `signal.capture()` (from `useSignal()`) and the function returned by `useSignalCapture()` have the same signature. Events are sent in batches; identity and session context are attached automatically. ## Example ```tsx theme={null} import { useSignalCapture } from '@signal-js/react'; function CheckoutButton({ orderId, amount }) { const capture = useSignalCapture(); const handleCheckout = () => { capture('checkout_started', { orderId, amount }); // ... then run checkout logic }; return ; } ``` ## See also * [Identify](/docs/integrations/react/identify) — Set user identity * [Page views](/docs/integrations/react/page-views) — Track page views and useTrackEvent # Configuration Source: https://docs.trysignal.ai/integrations/react/configuration SignalProvider and browser SDK options for React # React — Configuration Configure the React provider and the underlying browser SDK via `SignalProvider`'s `options` prop. Required: `apiKey`, `projectId`. Optional: `endpoint` (defaults to `https://api.trysignal.ai/`). ## Provider props | Prop | Type | Default | Description | | ----------- | --------------- | -------- | ---------------------------------------- | | `options` | `SignalOptions` | required | Browser SDK config (see below) | | `autoStart` | `boolean` | `true` | Start recording when the provider mounts | ## Browser SDK options `options` is passed to `createSignal`. Required: `apiKey`, `projectId`. Optional: `endpoint` (defaults to `https://api.trysignal.ai/`). ### Transport and batching | Option | Type | Default | Description | | -------------------- | ---------------------------------------- | ------- | --------------------------------- | | `compression` | `boolean` | `true` | Gzip events before sending | | `flushInterval` | `number` | `2000` | Flush interval in ms | | `maxBatchSize` | `number` | `50` | Max events per batch before flush | | `getSignedUploadUrl` | `(sessionId: string) => Promise` | — | Custom signed URL for uploads | ### Features | Option | Type | Default | Description | | --------------------------- | --------- | ------- | ------------------------------------------------ | | `enableSessionReplay` | `boolean` | `true` | Enable rrweb session replay | | `enableNetworkCapture` | `boolean` | `true` | Capture fetch/XHR in replay | | `enableConsoleCapture` | `boolean` | `true` | Capture console logs | | `enableSessionTracking` | `boolean` | `true` | Track page views, session start/end, performance | | `enablePerformanceTracking` | `boolean` | `true` | Track Web Vitals and performance metrics | | `enableBrowserDetection` | `boolean` | `true` | Run browser compatibility checks | | `debug` | `boolean` | `false` | Enable debug logging | ### sessionRecordingMasking Session recording masking controls how sensitive content appears in replays. The recorder (rrweb) applies these rules when capturing the DOM: **blocked** elements are removed or replaced with a placeholder; **masked text** is replaced with asterisks (or a custom string); **ignored inputs** do not record value changes (only focus/blur). You can target elements by CSS class, CSS selector, or (for custom logic) a function. **How the options work** * **blockClass** / **blockSelector** — Elements matching the class or selector are **fully hidden** in the replay (replaced with a placeholder). Use for entire sections you don’t want visible (e.g. sidebar with PII, payment forms). Default class: `signal-no-capture`. * **maskTextClass** / **maskTextSelector** — Text content of matching elements is **replaced with asterisks** (`***`) in the replay. Use for sensitive text (names, emails, IDs) that you still want to show layout for. Default class: `signal-mask`. * **maskTextFn** — Optional `(text, element?) => string`. Called for each masked text node; return the string to show in the replay (e.g. `'[REDACTED]'` or partial mask). If you return the original text, it is not masked. * **ignoreClass** — Inputs/textarea with this class do **not** have their values recorded; only focus and interaction are. Use when you want to hide what the user typed but keep the fact they used the field. Default class: `signal-ignore-input`. * **maskAllInputs** — When `true`, **all** `` and `