Skip to main content

Overview

This guide covers integrating the Stringboot Web SDK in Next.js applications for both the App Router (Next.js 13+) and Pages Router.

Installation

npm install @stringboot/web-sdk

App Router (Next.js 13+)

Setup Provider Component

Create a client-side provider component for Stringboot:
// app/providers/stringboot-provider.tsx
'use client';

import { useStringBoot } from '@stringboot/web-sdk/react';

const isDev = process.env.NODE_ENV === 'development';

export function StringBootProvider({ children }: { children: React.ReactNode }): React.ReactNode {
  const { initialized, error } = useStringBoot({
    apiToken: process.env.NEXT_PUBLIC_STRINGBOOT_API_TOKEN!,
    baseUrl: process.env.NEXT_PUBLIC_STRINGBOOT_API_URL || 'https://api.stringboot.com',
    defaultLanguage: 'en',
    debug: isDev,
  });

  if (error) {
    return (
      <div className="flex min-h-screen items-center justify-center">
        <div className="text-red-600">
          <h1 className="text-xl font-bold">StringBoot Error</h1>
          <p>{error}</p>
        </div>
      </div>
    );
  }

  if (!initialized) {
    return (
      <div className="flex min-h-screen items-center justify-center">
        <div className="text-center">
          <div className="animate-spin h-8 w-8 border-4 border-blue-500 border-t-transparent rounded-full mx-auto mb-4" />
          <p className="text-gray-600">Loading StringBoot...</p>
        </div>
      </div>
    );
  }

  return <>{children}</>;
}
The provider handles loading and error states with proper UI feedback. This prevents the app from rendering before strings are ready.

Add to Root Layout

Wrap your application with the StringBoot provider:
// app/layout.tsx
import { StringBootProvider } from './providers/stringboot-provider';
import './globals.css';

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

Use in Client Components

Create client components that use Stringboot hooks:
// app/page.tsx
'use client';

import { useString, useLanguage } from '@stringboot/web-sdk/react';

export default function Home() {
  const text = useString('welcome_message');
  const [lang, setLang] = useLanguage();

  return (
    <div className="flex min-h-screen items-center justify-center">
      <main className="flex flex-col items-center gap-8">
        {/* Language Switcher */}
        <div className="mb-8">
          <select
            value={lang}
            onChange={(e) => setLang(e.target.value)}
            className="border rounded px-4 py-2"
          >
            <option value="en">English</option>
            <option value="es">Español</option>
            <option value="fr">Français</option>
          </select>
        </div>

        {/* Dynamic String */}
        <h1 className="text-3xl font-semibold">
          {text || 'Loading...'}
        </h1>
      </main>
    </div>
  );
}
Use the || operator to provide a fallback value while strings are loading. The hooks automatically update the UI when strings become available.

Pages Router (Next.js 12 and below)

Create Provider Component

// components/StringBootProvider.tsx
import { useStringBoot } from '@stringboot/web-sdk/react';
import { ReactNode } from 'react';

interface StringBootProviderProps {
  children: ReactNode;
}

export function StringBootProvider({ children }: StringBootProviderProps) {
  const { initialized, error } = useStringBoot({
    apiToken: process.env.NEXT_PUBLIC_STRINGBOOT_API_TOKEN!,
    baseUrl: 'https://api.stringboot.com',
    defaultLanguage: 'en',
    debug: process.env.NODE_ENV === 'development',
  });

  if (error) {
    console.error('StringBoot initialization error:', error);
  }

  return <>{children}</>;
}

Add to _app.tsx

// pages/_app.tsx
import type { AppProps } from 'next/app';
import { StringBootProvider } from '../components/StringBootProvider';
import '../styles/globals.css';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <StringBootProvider>
      <Component {...pageProps} />
    </StringBootProvider>
  );
}

Use in Pages

// pages/index.tsx
import { useString, useLanguage } from '@stringboot/web-sdk/react';

export default function Home() {
  const title = useString('home_title');
  const description = useString('home_description');
  const [lang, setLang] = useLanguage();

  return (
    <div>
      <h1>{title}</h1>
      <p>{description}</p>

      <select value={lang} onChange={(e) => setLang(e.target.value)}>
        <option value="en">English</option>
        <option value="es">Español</option>
      </select>
    </div>
  );
}

Server-Side Rendering

Use getServerSideProps for dynamic server-side rendering:
import { GetServerSideProps } from 'next';
import StringBoot from '@stringboot/web-sdk';

interface PageProps {
  title: string;
  description: string;
}

export const getServerSideProps: GetServerSideProps<PageProps> = async () => {
  await StringBoot.initialize({
    apiToken: process.env.STRINGBOOT_API_TOKEN!,
    baseUrl: 'https://api.stringboot.com',
    defaultLanguage: 'en',
  });

  return {
    props: {
      title: await StringBoot.get('page_title'),
      description: await StringBoot.get('page_description'),
    },
  };
};

export default function Page({ title, description }: PageProps) {
  return (
    <div>
      <h1>{title}</h1>
      <p>{description}</p>
    </div>
  );
}

Static Generation

Use getStaticProps for static generation with revalidation:
import { GetStaticProps } from 'next';
import StringBoot from '@stringboot/web-sdk';

interface PageProps {
  strings: {
    [key: string]: string;
  };
}

export const getStaticProps: GetStaticProps<PageProps> = async () => {
  await StringBoot.initialize({
    apiToken: process.env.STRINGBOOT_API_TOKEN!,
    baseUrl: 'https://api.stringboot.com',
    defaultLanguage: 'en',
  });

  return {
    props: {
      strings: {
        title: await StringBoot.get('page_title'),
        subtitle: await StringBoot.get('page_subtitle'),
        cta: await StringBoot.get('cta_button'),
      },
    },
    revalidate: 3600, // Revalidate every hour
  };
};

export default function Page({ strings }: PageProps) {
  return (
    <div>
      <h1>{strings.title}</h1>
      <h2>{strings.subtitle}</h2>
      <button>{strings.cta}</button>
    </div>
  );
}

Environment Variables

Create a .env.local file in your project root:
# Required: API Token (client-side)
NEXT_PUBLIC_STRINGBOOT_API_TOKEN=your_token_here

# Optional: Custom API URL (defaults to https://api.stringboot.com)
NEXT_PUBLIC_STRINGBOOT_API_URL=https://api.stringboot.com

# Optional: Server-side only token (more secure for SSR/SSG)
STRINGBOOT_API_TOKEN=your_token_here
  • Use NEXT_PUBLIC_* prefix for client-side usage (required for client components)
  • Use regular env vars for server-side only (SSR, SSG, API routes) to keep tokens secure
  • Never commit .env.local to version control - add it to .gitignore

Common Patterns

Language Switcher

'use client'; // for App Router

import { useLanguage, useActiveLanguages } from '@stringboot/web-sdk/react';

export function LanguageSwitcher() {
  const [currentLang, setLanguage] = useLanguage();
  const { languages, loading, error } = useActiveLanguages();

  if (loading) return <div>Loading languages...</div>;
  if (error) return null;

  return (
    <select
      value={currentLang}
      onChange={(e) => setLanguage(e.target.value)}
      className="border rounded px-3 py-2"
    >
      {languages.map((lang) => (
        <option key={lang.code} value={lang.code}>
          {lang.name}
        </option>
      ))}
    </select>
  );
}

Multiple Strings

'use client';

import { useStrings } from '@stringboot/web-sdk/react';

export function ProductCard() {
  const strings = useStrings([
    'product_name',
    'product_description',
    'add_to_cart',
    'price_label',
  ]);

  return (
    <div className="card">
      <h3>{strings.product_name}</h3>
      <p>{strings.product_description}</p>
      <div>
        <span>{strings.price_label}</span>
        <button>{strings.add_to_cart}</button>
      </div>
    </div>
  );
}

Dynamic Metadata (App Router)

// app/page.tsx
import { Metadata } from 'next';
import StringBoot from '@stringboot/web-sdk';

export async function generateMetadata(): Promise<Metadata> {
  await StringBoot.initialize({
    apiToken: process.env.STRINGBOOT_API_TOKEN!,
    baseUrl: 'https://api.stringboot.com',
    defaultLanguage: 'en',
  });

  return {
    title: await StringBoot.get('meta_title'),
    description: await StringBoot.get('meta_description'),
  };
}

export default function Page() {
  return <div>Page content</div>;
}

Troubleshooting

Hydration Mismatch Error

Cause: Server-rendered content doesn’t match client-rendered content. Solution: Ensure consistent initialization on both server and client, or use client-only rendering for dynamic content.

Strings Not Updating

Cause: Not using reactive hooks. Solution: Use useString instead of direct StringBoot.get():
const [text, setText] = useState('');
useEffect(() => {
  StringBoot.get('key').then(setText);
}, []); // Won't update on language change

Environment Variables Undefined

Cause: Missing NEXT_PUBLIC_ prefix for client-side usage. Solution:
  • Use NEXT_PUBLIC_* for client components
  • Use regular env vars for server components

Module Not Found Error

Cause: Trying to import React hooks in server component. Solution: Add 'use client' directive:
'use client';

import { useString } from '@stringboot/web-sdk/react';

Performance Tips

Preload Critical Strings

// app/layout.tsx
import StringBoot from '@stringboot/web-sdk';

async function preloadStrings() {
  await StringBoot.initialize({
    apiToken: process.env.STRINGBOOT_API_TOKEN!,
    baseUrl: 'https://api.stringboot.com',
    defaultLanguage: 'en',
  });

  // Preload critical strings
  await Promise.all([
    StringBoot.get('nav_home'),
    StringBoot.get('nav_about'),
    StringBoot.get('nav_contact'),
  ]);
}

export default async function RootLayout({ children }) {
  await preloadStrings();

  return (
    <html>
      <body>
        <StringBootProvider>{children}</StringBootProvider>
      </body>
    </html>
  );
}

Use Static Generation

For content that doesn’t change frequently:
export const revalidate = 3600; // Revalidate every hour

export default async function Page() {
  const title = await StringBoot.get('title');
  return <h1>{title}</h1>;
}

Best Practices

Initialize the SDK once in your root layout or _app file, not in every component.
Always use useString and other hooks for automatic updates when language changes.
Use NEXT_PUBLIC_* for client-side and regular env vars for server-side for better security.
Always check for initialization errors and provide fallback UI or default strings.

What’s Next?