Skip to content

Next.js Integration

Learn how to integrate Sibilance voice surveys with Next.js applications.

Overview

Sibilance works seamlessly with Next.js, supporting both App Router and Pages Router patterns.

Installation

bash
npm install @sibilance.is/client

App Router (Next.js 13+)

Quick Start

tsx
// app/components/VoiceSurvey.tsx
'use client';

import { useSibilance, FloatingMicButton } from '@sibilance.is/client/react';

export function VoiceSurvey() {
  const { voiceState, surveyState, toggleSession } = useSibilance({
    surveyKey: process.env.NEXT_PUBLIC_SIBILANCE_SURVEY_KEY!,
  }, {
    onComplete: async (yaml) => {
      console.log('Survey completed!', yaml);
      
      // Submit to your API route
      await fetch('/api/survey-results', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ results: yaml })
      });
    }
  });

  return (
    <FloatingMicButton
      isConnected={voiceState.isConnected}
      onClick={toggleSession}
      position="bottom-right"
    />
  );
}
tsx
// app/layout.tsx
import { VoiceSurvey } from './components/VoiceSurvey';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        {children}
        <VoiceSurvey />
      </body>
    </html>
  );
}

Pages Router (Next.js 12 and below)

Quick Start

tsx
// pages/_app.tsx
import { useSibilance, FloatingMicButton } from '@sibilance.is/client/react';
import type { AppProps } from 'next/app';

function MyApp({ Component, pageProps }: AppProps) {
  const { voiceState, surveyState, toggleSession } = useSibilance({
    surveyKey: process.env.NEXT_PUBLIC_SIBILANCE_SURVEY_KEY!,
  }, {
    onComplete: async (yaml) => {
      // Submit to API route
      await fetch('/api/survey-results', {
        method: 'POST',
        body: JSON.stringify({ results: yaml })
      });
    }
  });

  return (
    <>
      <Component {...pageProps} />
      <FloatingMicButton
        isConnected={voiceState.isConnected}
        onClick={toggleSession}
        position="bottom-right"
      />
    </>
  );
}

export default MyApp;

Complete Example

tsx
// app/components/VoiceSurvey.tsx
'use client';

import { useSibilance, FloatingMicButton } from '@sibilance.is/client/react';
import { useEffect, useState } from 'react';

export function VoiceSurvey() {
  const [results, setResults] = useState<any[]>([]);
  
  const { voiceState, surveyState, toggleSession } = useSibilance({
    surveyKey: process.env.NEXT_PUBLIC_SIBILANCE_SURVEY_KEY!,
  }, {
    onComplete: async (yaml) => {
      console.log('Survey completed!', yaml);
      setResults(yaml);
      
      // Submit to API route
      const response = await fetch('/api/survey-results', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ results: yaml })
      });
      
      if (response.ok) {
        console.log('Results submitted successfully!');
      }
    },
    onRecordInformation: (info) => {
      console.log('Recorded:', info.field, '=', info.value);
    },
    onError: (error) => {
      console.error('Survey error:', error);
    }
  });

  return (
    <>
      {/* Survey status */}
      {surveyState.isActive && (
        <div className="survey-status">
          <p>Survey in progress...</p>
          <p>Current step: {surveyState.currentStepId}</p>
        </div>
      )}
      
      {/* Floating microphone button */}
      <FloatingMicButton
        isConnected={voiceState.isConnected}
        onClick={toggleSession}
        position="bottom-right"
      />
    </>
  );
}
tsx
// app/layout.tsx
import { VoiceSurvey } from './components/VoiceSurvey';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        {children}
        <VoiceSurvey />
      </body>
    </html>
  );
}

API Routes Integration

Create an API route to handle survey results:

typescript
// app/api/survey-results/route.ts
import { NextResponse } from 'next/server';

export async function POST(request: Request) {
  try {
    const { results } = await request.json();
    
    // Validate results
    if (!Array.isArray(results)) {
      return NextResponse.json(
        { error: 'Invalid results format' },
        { status: 400 }
      );
    }
    
    // Process results (save to database, send email, etc.)
    await processSurveyResults(results);
    
    return NextResponse.json({ success: true });
  } catch (error) {
    console.error('Failed to process survey results:', error);
    return NextResponse.json(
      { error: 'Failed to process results' },
      { status: 500 }
    );
  }
}

async function processSurveyResults(results: any[]) {
  // Your processing logic
  // e.g., save to database, send webhook, etc.
}

Server Components

Sibilance works with Next.js server components by using client components for voice functionality:

tsx
// app/page.tsx (Server Component)
import { ProductList } from './components/ProductList';
import { VoiceSurvey } from './components/VoiceSurvey';

export default async function Home() {
  const products = await fetchProducts();

  return (
    <main>
      <h1>Products</h1>
      <ProductList products={products} />
      <VoiceSurvey />
    </main>
  );
}
tsx
// app/components/VoiceSurvey.tsx (Client Component)
'use client';

import { useSibilance, FloatingMicButton } from '@sibilance.is/client/react';

export function VoiceSurvey() {
  const { voiceState, toggleSession } = useSibilance({
    surveyKey: process.env.NEXT_PUBLIC_SIBILANCE_SURVEY_KEY!,
  });

  return (
    <FloatingMicButton
      isConnected={voiceState.isConnected}
      onClick={toggleSession}
    />
  );
}

Environment Variables

Store your Survey Key securely:

bash
# .env.local
NEXT_PUBLIC_SIBILANCE_SURVEY_KEY=sibilance_your_key_here
tsx
const { voiceState, toggleSession } = useSibilance({
  surveyKey: process.env.NEXT_PUBLIC_SIBILANCE_SURVEY_KEY!,
});

Best Practices

  1. Use Client Components - Sibilance requires client-side JavaScript
  2. Environment Variables - Store Survey Key in environment variables
  3. Server Components - Keep voice logic in client components
  4. Error Boundaries - Wrap Sibilance components in error boundaries
  5. Loading States - Handle loading states during survey initialization
  6. API Routes - Use API routes for submitting survey results

Troubleshooting

"useRouter must be used in a Client Component"

Make sure your Sibilance setup is in a client component:

tsx
'use client';  // Add this at the top

import { useSibilance } from '@sibilance.is/client/react';

Hydration Errors

Avoid using Sibilance state during SSR:

tsx
function MyComponent() {
  const { surveyState } = useSibilance({
    surveyKey: process.env.NEXT_PUBLIC_SIBILANCE_SURVEY_KEY!
  });
  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    setMounted(true);
  }, []);

  if (!mounted) return null;

  return <div>{surveyState.isActive && 'Survey active'}</div>;
}

Survey Not Loading

  1. Verify Survey Key is correct
  2. Check environment variable is set
  3. Verify survey is active in Sibilance platform
  4. Check domain restrictions allow your domain
  5. Check browser console for errors