Skip to content

Next.js Examples ​

Complete examples for using Flowfull Clients with Next.js applications (App Router & Pages Router).

Architecture

  • @pubflow/nextjs - For authentication with Flowless (managed auth service)
  • @pubflow/flowfull-client - For HTTP requests to your custom Flowfull backend

Installation ​

bash
# Install both packages
npm install @pubflow/nextjs @pubflow/flowfull-client

App Router Setup ​

1. Configure PubflowProvider ​

typescript
// app/providers.tsx
'use client';

import { PubflowProvider } from '@pubflow/nextjs';

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <PubflowProvider
      config={{
        baseUrl: process.env.NEXT_PUBLIC_FLOWLESS_URL || 'https://your-flowless.pubflow.com',
        bridgeBasePath: '/bridge',
        authBasePath: '/auth'
      }}
      loginRedirectPath="/login"
      publicPaths={['/login', '/register']}
    >
      {children}
    </PubflowProvider>
  );
}
typescript
// app/layout.tsx
import { Providers } from './providers';

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

2. Create Flowfull API Client ​

typescript
// lib/flowfull.ts
import { createFlowfull } from '@pubflow/flowfull-client';

// Create client for your custom backend
export const flowfull = createFlowfull(
  process.env.NEXT_PUBLIC_BACKEND_URL || 'https://api.myapp.com',
  {
    headers: {
      'X-Client-Version': '1.0.0'
    },
    retryAttempts: 3,
    timeout: 30000
  }
);

Authentication with Flowless ​

Login Page ​

typescript
// app/login/page.tsx
'use client';

import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { useAuth } from '@pubflow/nextjs';

export default function LoginPage() {
  const router = useRouter();
  const { login, isLoading } = useAuth();
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    setError('');

    try {
      const result = await login({ email, password });

      if (result.success) {
        router.push('/dashboard');
      } else {
        setError(result.error || 'Login failed');
      }
    } catch (err) {
      setError('An error occurred');
    }
  }

  return (
    <div className="login-page">
      <h1>Login</h1>
      <form onSubmit={handleSubmit}>
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value.toLowerCase())}
          placeholder="Email"
          required
        />
        <input
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          placeholder="Password"
          required
        />
        <button type="submit" disabled={isLoading}>
          {isLoading ? 'Logging in...' : 'Login'}
        </button>
        {error && <div className="error">{error}</div>}
      </form>
    </div>
  );
}

Dashboard Page ​

typescript
// app/dashboard/page.tsx
'use client';

import { useAuth } from '@pubflow/nextjs';
import { useRouter } from 'next/navigation';

export default function DashboardPage() {
  const { user, isAuthenticated, logout, isLoading } = useAuth();
  const router = useRouter();

  async function handleLogout() {
    await logout();
    router.push('/login');
  }

  if (isLoading) {
    return <div>Loading...</div>;
  }

  if (!isAuthenticated) {
    router.push('/login');
    return null;
  }

  return (
    <div className="dashboard">
      <h1>Dashboard</h1>
      <div className="user-info">
        <p><strong>Name:</strong> {user?.name}</p>
        <p><strong>Email:</strong> {user?.email}</p>
        <p><strong>User Type:</strong> {user?.userType}</p>
      </div>
      <button onClick={handleLogout}>Logout</button>
    </div>
  );
}

Protected Page with useAuthGuard ​

typescript
// app/admin/page.tsx
'use client';

import { useAuthGuard } from '@pubflow/nextjs';
import { useRouter } from 'next/navigation';

export default function AdminPage() {
  const router = useRouter();

  const { user, isLoading } = useAuthGuard({
    allowedTypes: ['admin'],
    onRedirect: (path, reason) => {
      if (reason === 'unauthorized') {
        alert('Admin access required');
      }
      router.push(path);
    }
  });

  if (isLoading) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <h1>Admin Panel</h1>
      <p>Welcome, Admin {user?.name}!</p>
    </div>
  );
}

Data Fetching from Custom Backend ​

Use @pubflow/flowfull-client to fetch data from your custom Flowfull backend:

User List Page ​

typescript
// app/users/page.tsx
'use client';

import { useState, useEffect } from 'react';
import { flowfull } from '@/lib/flowfull';

interface User {
  id: string;
  name: string;
  email: string;
  role: string;
}

export default function UsersPage() {
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');

  useEffect(() => {
    fetchUsers();
  }, []);

  async function fetchUsers() {
    try {
      const data = await flowfull.get<User[]>('/users');
      setUsers(data);
    } catch (err: any) {
      setError(err.message || 'Failed to fetch users');
    } finally {
      setLoading(false);
    }
  }

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <div>
      <h1>Users</h1>
      <ul>
        {users.map(user => (
          <li key={user.id}>
            <strong>{user.name}</strong> - {user.email} ({user.role})
          </li>
        ))}
      </ul>
    </div>
  );
}

Pages Router Setup ​

Configure _app.tsx ​

typescript
// pages/_app.tsx
import type { AppProps } from 'next/app';
import { PubflowProvider } from '@pubflow/nextjs';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <PubflowProvider
      config={{
        baseUrl: process.env.NEXT_PUBLIC_FLOWLESS_URL || 'https://your-flowless.pubflow.com',
        bridgeBasePath: '/bridge',
        authBasePath: '/auth'
      }}
      loginRedirectPath="/login"
      publicPaths={['/login', '/register']}
    >
      <Component {...pageProps} />
    </PubflowProvider>
  );
}

Login Page (Pages Router) ​

typescript
// pages/login.tsx
import { useState } from 'react';
import { useRouter } from 'next/router';
import { useAuth } from '@pubflow/nextjs';

export default function LoginPage() {
  const router = useRouter();
  const { login, isLoading } = useAuth();
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    setError('');

    try {
      const result = await login({ email, password });

      if (result.success) {
        router.push('/dashboard');
      } else {
        setError(result.error || 'Login failed');
      }
    } catch (err) {
      setError('An error occurred');
    }
  }

  return (
    <div>
      <h1>Login</h1>
      <form onSubmit={handleSubmit}>
        <input
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value.toLowerCase())}
          placeholder="Email"
          required
        />
        <input
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          placeholder="Password"
          required
        />
        <button type="submit" disabled={isLoading}>
          {isLoading ? 'Logging in...' : 'Login'}
        </button>
        {error && <div>{error}</div>}
      </form>
    </div>
  );
}

Next Steps ​