Skip to content

Getting Started

Get up and running with Flowfull Clients in minutes. This guide will walk you through installation, setup, and your first API call.

🚀 Fastest Way to Start

Want to skip the setup? Use our Official Starter Kits - production-ready templates with everything configured:

Prerequisites

Before you begin, make sure you have:

  • Node.js 18+ installed
  • A Flowfull backend deployed and running (Create one)
  • Flowless authentication configured in your backend (Setup Flowless)
  • Basic knowledge of React, React Native, or Next.js

📋 Backend Required

Flowfull Clients are frontend libraries that need a Flowfull backend to connect to. You have two options:

  1. Use a Starter Kit (Recommended) - Pre-configured frontend with example backend connection

  2. Build from Scratch - Create your complete stack:

    • Step 1: Create Flowless Instance - Authentication backend (managed service)
    • Step 2: Build Flowfull Backend - Your custom backend with business logic (using flowfull-node)
    • Step 3: Install Flowfull Clients (this guide) - Your frontend that consumes your Flowfull backend

Architecture: Frontend (Flowfull Clients)Backend (Flowfull)Auth (Flowless)

💳 Need Payments?

Add payment processing with Bridge Payments - Works seamlessly with Flowfull Clients!

Choose Your Framework

Select the guide that matches your project:

bash
npm install @pubflow/core @pubflow/react swr
bash
npm install @pubflow/core @pubflow/react-native swr
npm install @react-native-async-storage/async-storage
bash
npm install @pubflow/core @pubflow/nextjs swr

Quick Start: React

1. Setup Provider

Wrap your app with PubflowProvider:

typescript
// src/App.tsx
import { PubflowProvider } from '@pubflow/react';

function App() {
  return (
    <PubflowProvider url="https://your-backend.com">
      <YourApp />
    </PubflowProvider>
  );
}

2. Add Authentication

Create a login component:

typescript
// src/components/Login.tsx
import { useAuth } from '@pubflow/react';
import { useState } from 'react';

export function Login() {
  const { login, isLoading } = useAuth();
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    try {
      await login({ email, password });
      // User is now logged in!
    } catch (error) {
      console.error('Login failed:', error);
    }
  };

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

3. Fetch Data

Use useBridgeQuery to fetch data from your backend:

typescript
// src/components/UserList.tsx
import { useBridgeQuery } from '@pubflow/react';

export function UserList() {
  const { data, error, isLoading } = useBridgeQuery('/api/users');

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

  return (
    <ul>
      {data.map((user: any) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

4. Create Data

Use useBridgeMutation to create or update data:

typescript
// src/components/CreateUser.tsx
import { useBridgeMutation } from '@pubflow/react';
import { useState } from 'react';

export function CreateUser() {
  const { trigger, isMutating } = useBridgeMutation('/api/users', 'POST');
  const [name, setName] = useState('');

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    try {
      const newUser = await trigger({ name });
      console.log('User created:', newUser);
      setName('');
    } catch (error) {
      console.error('Failed to create user:', error);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Name"
      />
      <button type="submit" disabled={isMutating}>
        {isMutating ? 'Creating...' : 'Create User'}
      </button>
    </form>
  );
}

Quick Start: React Native

1. Setup Provider

typescript
// App.tsx
import { PubflowProvider } from '@pubflow/react-native';
import { SafeAreaView } from 'react-native';

export default function App() {
  return (
    <PubflowProvider url="https://your-backend.com">
      <SafeAreaView style={{ flex: 1 }}>
        <YourApp />
      </SafeAreaView>
    </PubflowProvider>
  );
}

2. Add Authentication

typescript
// screens/LoginScreen.tsx
import { useAuth } from '@pubflow/react-native';
import { useState } from 'react';
import { View, TextInput, Button, Text } from 'react-native';

export function LoginScreen() {
  const { login, isLoading, error } = useAuth();
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleLogin = async () => {
    try {
      await login({ email: email.toLowerCase(), password });
    } catch (err) {
      console.error('Login failed:', err);
    }
  };

  return (
    <View style={{ padding: 20 }}>
      <TextInput
        value={email}
        onChangeText={(text) => setEmail(text.toLowerCase())}
        placeholder="Email"
        keyboardType="email-address"
        autoCapitalize="none"
      />
      <TextInput
        value={password}
        onChangeText={setPassword}
        placeholder="Password"
        secureTextEntry
      />
      {error && <Text style={{ color: 'red' }}>{error.message}</Text>}
      <Button
        title={isLoading ? 'Logging in...' : 'Login'}
        onPress={handleLogin}
        disabled={isLoading}
      />
    </View>
  );
}

Quick Start: Next.js

1. Setup Provider (Client Component)

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

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

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <PubflowProvider url={process.env.NEXT_PUBLIC_BACKEND_URL!}>
      {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. Add Authentication

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

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

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

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    try {
      await login({ email: email.toLowerCase(), password });
      router.push('/dashboard');
    } catch (error) {
      console.error('Login failed:', error);
    }
  };

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

3. Server-Side Authentication

typescript
// app/dashboard/page.tsx
import { withPubflowAuthSSR } from '@pubflow/nextjs';

export default withPubflowAuthSSR(
  async function DashboardPage({ user }) {
    return (
      <div>
        <h1>Welcome, {user.name}!</h1>
        <p>Email: {user.email}</p>
      </div>
    );
  },
  {
    redirectTo: '/login',
  }
);

4. API Routes with Authentication

typescript
// app/api/users/route.ts
import { withPubflow } from '@pubflow/nextjs';

export const GET = withPubflow(async (req, { user }) => {
  // user is automatically available if authenticated
  if (!user) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 });
  }

  // Your logic here
  return Response.json({ users: [] });
});

Environment Variables

Create a .env.local file in your project root:

bash
VITE_BACKEND_URL=https://your-backend.com
bash
EXPO_PUBLIC_BACKEND_URL=https://your-backend.com
bash
NEXT_PUBLIC_BACKEND_URL=https://your-backend.com

Configuration Options

The PubflowProvider accepts several configuration options:

typescript
<PubflowProvider
  url="https://your-backend.com"
  instanceId="default"           // Optional: for multi-instance support
  onAuthError={(error) => {      // Optional: handle auth errors
    console.error('Auth error:', error);
  }}
  onSessionExpired={() => {      // Optional: handle session expiration
    console.log('Session expired');
  }}
>
  <App />
</PubflowProvider>

Next Steps

Now that you have the basics working, explore more features:

Common Patterns

Protected Routes

typescript
import { useAuth } from '@pubflow/react';
import { Navigate } from 'react-router-dom';

function ProtectedRoute({ children }: { children: React.ReactNode }) {
  const { isAuthenticated, isLoading } = useAuth();

  if (isLoading) return <div>Loading...</div>;
  if (!isAuthenticated) return <Navigate to="/login" />;

  return <>{children}</>;
}

Logout Button

typescript
import { useAuth } from '@pubflow/react';

function LogoutButton() {
  const { logout, user } = useAuth();

  return (
    <div>
      <span>Welcome, {user?.name}</span>
      <button onClick={logout}>Logout</button>
    </div>
  );
}

Error Handling

typescript
import { useBridgeQuery } from '@pubflow/react';

function UserProfile({ userId }: { userId: string }) {
  const { data, error, isLoading } = useBridgeQuery(`/api/users/${userId}`);

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

  if (error) {
    return (
      <div>
        <h3>Error loading user</h3>
        <p>{error.message}</p>
        <button onClick={() => window.location.reload()}>Retry</button>
      </div>
    );
  }

  return <div>{data.name}</div>;
}

Troubleshooting

CORS Issues

If you're getting CORS errors, make sure your Flowfull backend has CORS configured:

typescript
// In your Flowfull backend
app.use(cors({
  origin: ['http://localhost:3000', 'https://your-frontend.com'],
  credentials: true,
}));

Session Not Persisting

Make sure you're using the correct storage adapter for your platform:

  • React: LocalStorage (automatic)
  • React Native: AsyncStorage (install @react-native-async-storage/async-storage)
  • Next.js: Cookies (automatic)

TypeScript Errors

If you're getting TypeScript errors, make sure you have the latest type definitions:

bash
npm install --save-dev @types/react @types/node

Need Help?