Skip to content

@pubflow/react ​

React integration for Flowfull Clients with hooks, components, and SWR-based data fetching.

Works With Any React Framework

This package works with any React-based framework:

  • ✅ Create React App (CRA)
  • ✅ Vite + React
  • ✅ TanStack Start (React Router)
  • ✅ Remix (client-side)
  • ✅ Astro (React islands)
  • ✅ Any other React framework

For Next.js, use @pubflow/nextjs instead for SSR support and API routes.

Installation ​

bash
npm install @pubflow/core @pubflow/react swr

Quick Start ​

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

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

function Dashboard() {
  const { user, logout } = useAuth();
  const { data, error, isLoading } = useBridgeQuery('/api/users');

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

  return (
    <div>
      <h1>Welcome, {user?.name}!</h1>
      <button onClick={logout}>Logout</button>
      <ul>
        {data.map((user: any) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
}

Provider ​

PubflowProvider ​

Wrap your app with PubflowProvider to configure Flowfull Clients.

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

<PubflowProvider
  url="https://your-backend.com"
  instanceId="default"
  onAuthError={(error) => console.error('Auth error:', error)}
  onSessionExpired={() => console.log('Session expired')}
>
  <App />
</PubflowProvider>

Props ​

PropTypeRequiredDescription
urlstringYesYour Flowfull backend URL
instanceIdstringNoInstance ID for multi-instance support
onAuthError(error: Error) => voidNoCalled when authentication errors occur
onSessionExpired() => voidNoCalled when session expires
childrenReactNodeYesYour app components

Hooks ​

useAuth() ​

Access authentication state and methods.

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

function LoginForm() {
  const {
    user,
    isAuthenticated,
    isLoading,
    error,
    login,
    logout,
    register,
  } = useAuth();

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

  return (
    <div>
      {isAuthenticated ? (
        <>
          <p>Welcome, {user?.name}!</p>
          <button onClick={logout}>Logout</button>
        </>
      ) : (
        <button onClick={handleLogin} disabled={isLoading}>
          Login
        </button>
      )}
    </div>
  );
}

Return Value ​

typescript
{
  user: User | null;
  isAuthenticated: boolean;
  isLoading: boolean;
  error: Error | null;
  login: (credentials: LoginCredentials) => Promise<User>;
  register: (data: RegisterData) => Promise<User>;
  logout: () => Promise<void>;
  refreshSession: () => Promise<void>;
}

useBridgeQuery() ​

Fetch data from your Flowfull backend with SWR.

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

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

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

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

Parameters ​

typescript
useBridgeQuery<T>(
  path: string,
  options?: {
    params?: Record<string, any>;
    swrOptions?: SWRConfiguration;
  }
)

Return Value ​

typescript
{
  data: T | undefined;
  error: Error | undefined;
  isLoading: boolean;
  isValidating: boolean;
  mutate: () => Promise<void>;
}

useBridgeMutation() ​

Create, update, or delete data with optimistic updates.

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

function CreateUserForm() {
  const { trigger, isMutating, error } = 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 (err) {
      console.error('Failed to create user:', err);
    }
  };

  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>
      {error && <p>Error: {error.message}</p>}
    </form>
  );
}

Parameters ​

typescript
useBridgeMutation<T>(
  path: string,
  method: 'POST' | 'PUT' | 'PATCH' | 'DELETE',
  options?: {
    revalidate?: string[]; // Paths to revalidate after mutation
    optimisticData?: (current: any, data: any) => any;
  }
)

Return Value ​

typescript
{
  trigger: (data?: any) => Promise<T>;
  isMutating: boolean;
  error: Error | undefined;
  reset: () => void;
}

useBridgeCrud() ​

Complete CRUD operations for a resource.

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

function UserManager() {
  const {
    items,
    isLoading,
    error,
    create,
    update,
    remove,
    refresh,
  } = useBridgeCrud('/api/users');

  const handleCreate = async () => {
    await create({ name: 'New User' });
  };

  const handleUpdate = async (id: string) => {
    await update(id, { name: 'Updated Name' });
  };

  const handleDelete = async (id: string) => {
    await remove(id);
  };

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

  return (
    <div>
      <button onClick={handleCreate}>Create User</button>
      <button onClick={refresh}>Refresh</button>
      <ul>
        {items.map((user: any) => (
          <li key={user.id}>
            {user.name}
            <button onClick={() => handleUpdate(user.id)}>Edit</button>
            <button onClick={() => handleDelete(user.id)}>Delete</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

Return Value ​

typescript
{
  items: T[];
  isLoading: boolean;
  error: Error | undefined;
  create: (data: Partial<T>) => Promise<T>;
  update: (id: string, data: Partial<T>) => Promise<T>;
  remove: (id: string) => Promise<void>;
  refresh: () => Promise<void>;
}

useBridgeApi() ​

Direct access to the API client for custom requests.

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

function CustomComponent() {
  const api = useBridgeApi();

  const handleCustomRequest = async () => {
    const response = await api.post('/api/custom-endpoint', {
      custom: 'data',
    });
    console.log('Response:', response);
  };

  return <button onClick={handleCustomRequest}>Custom Request</button>;
}

Components ​

BridgeView ​

Display data from an API endpoint with automatic loading and error states.

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

<BridgeView
  endpoint="/api/users"
  render={(data) => (
    <ul>
      {data.map((user: any) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  )}
  loading={<div>Loading users...</div>}
  error={(error) => <div>Error: {error.message}</div>}
/>

Props ​

PropTypeRequiredDescription
endpointstringYesAPI endpoint to fetch from
render(data: T) => ReactNodeYesRender function for data
loadingReactNodeNoLoading state component
error(error: Error) => ReactNodeNoError state component
paramsRecord<string, any>NoQuery parameters

BridgeTable ​

Display data in a table with sorting, filtering, and pagination.

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

<BridgeTable
  endpoint="/api/users"
  columns={[
    { key: 'name', label: 'Name', sortable: true },
    { key: 'email', label: 'Email', sortable: true },
    { key: 'created_at', label: 'Created', sortable: true },
  ]}
  onRowClick={(user) => console.log('Clicked:', user)}
  searchable
  pagination
/>

Props ​

PropTypeRequiredDescription
endpointstringYesAPI endpoint to fetch from
columnsColumn[]YesTable columns configuration
onRowClick(row: T) => voidNoRow click handler
searchablebooleanNoEnable search
paginationbooleanNoEnable pagination
pageSizenumberNoItems per page (default: 10)

BridgeForm ​

Form component with automatic validation and API integration.

typescript
import { BridgeForm } from '@pubflow/react';
import { z } from 'zod';

const userSchema = z.object({
  name: z.string().min(2, 'Name must be at least 2 characters'),
  email: z.string().email('Invalid email address'),
  age: z.number().min(18, 'Must be at least 18 years old'),
});

<BridgeForm
  endpoint="/api/users"
  method="POST"
  schema={userSchema}
  onSuccess={(data) => console.log('User created:', data)}
  onError={(error) => console.error('Error:', error)}
  fields={[
    { name: 'name', label: 'Name', type: 'text' },
    { name: 'email', label: 'Email', type: 'email' },
    { name: 'age', label: 'Age', type: 'number' },
  ]}
  submitLabel="Create User"
/>

Props ​

PropTypeRequiredDescription
endpointstringYesAPI endpoint to submit to
method'POST' | 'PUT' | 'PATCH'YesHTTP method
schemaZodSchemaNoZod validation schema
fieldsField[]YesForm fields configuration
onSuccess(data: T) => voidNoSuccess callback
onError(error: Error) => voidNoError callback
submitLabelstringNoSubmit button label
initialValuesRecord<string, any>NoInitial form values

BridgeList ​

Display a list of items with infinite scroll and pull-to-refresh.

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

<BridgeList
  endpoint="/api/users"
  renderItem={(user) => (
    <div key={user.id}>
      <h3>{user.name}</h3>
      <p>{user.email}</p>
    </div>
  )}
  emptyMessage="No users found"
  infiniteScroll
  pullToRefresh
/>

Props ​

PropTypeRequiredDescription
endpointstringYesAPI endpoint to fetch from
renderItem(item: T) => ReactNodeYesRender function for each item
emptyMessagestringNoMessage when list is empty
infiniteScrollbooleanNoEnable infinite scroll
pullToRefreshbooleanNoEnable pull-to-refresh
pageSizenumberNoItems per page (default: 20)

OfflineIndicator ​

Display connection status indicator.

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

<OfflineIndicator
  position="top"
  message="You are offline"
  style={{ background: '#ff0000', color: '#fff' }}
/>

AdvancedFilter ​

Advanced filtering component for data tables.

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

<AdvancedFilter
  fields={[
    { name: 'name', label: 'Name', type: 'text' },
    { name: 'status', label: 'Status', type: 'select', options: ['active', 'inactive'] },
    { name: 'created_at', label: 'Created', type: 'date' },
  ]}
  onFilter={(filters) => console.log('Filters:', filters)}
/>

Examples ​

Complete CRUD Example ​

typescript
import { PubflowProvider, useBridgeCrud, BridgeForm } from '@pubflow/react';
import { useState } from 'react';
import { z } from 'zod';

const userSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
});

function UserManagement() {
  const { items, isLoading, create, update, remove } = useBridgeCrud('/api/users');
  const [editing, setEditing] = useState<string | null>(null);

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

  return (
    <div>
      <h2>Create User</h2>
      <BridgeForm
        endpoint="/api/users"
        method="POST"
        schema={userSchema}
        fields={[
          { name: 'name', label: 'Name', type: 'text' },
          { name: 'email', label: 'Email', type: 'email' },
        ]}
        onSuccess={() => console.log('User created')}
      />

      <h2>Users</h2>
      <ul>
        {items.map((user: any) => (
          <li key={user.id}>
            {editing === user.id ? (
              <BridgeForm
                endpoint={`/api/users/${user.id}`}
                method="PUT"
                schema={userSchema}
                fields={[
                  { name: 'name', label: 'Name', type: 'text' },
                  { name: 'email', label: 'Email', type: 'email' },
                ]}
                initialValues={user}
                onSuccess={() => setEditing(null)}
              />
            ) : (
              <>
                {user.name} - {user.email}
                <button onClick={() => setEditing(user.id)}>Edit</button>
                <button onClick={() => remove(user.id)}>Delete</button>
              </>
            )}
          </li>
        ))}
      </ul>
    </div>
  );
}

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

TypeScript Support ​

All hooks and components are fully typed:

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

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

function UserList() {
  const { data, error, isLoading } = useBridgeQuery<User[]>('/api/users');

  // data is typed as User[] | undefined
  // TypeScript will catch errors
}

Next Steps ​