Skip to main content

React Client — Getting Started

The Rhino React client provides TanStack Query hooks for every server endpoint. One hook per operation — no manual fetch calls, no boilerplate.

Start here — this page summarizes the whole client

This is the entry point for the React client docs. The Feature Map below is a complete summary of every hook, option and export the client ships, each linked to its deep-dive page. If you are an AI agent picking up this codebase, read this page first — it tells you what exists so you never hand-write a fetch call the client already covers.

Requirements​

  • React 18+ or 19+
  • TanStack React Query 5+
  • Axios 1+

Installation​

terminal
npm install @rhino-dev/rhino-react@^4.0 @tanstack/react-query axios

Setup​

1. Configure the API client​

Point the client to your Laravel backend:

src/config.ts
import { configureApi } from '@rhino-dev/rhino-react';

configureApi({
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:8000/api',
});
React Native

A React Native app authenticates with a bearer token, so turn cookies off and set a timeout:

src/config.ts
configureApi({
baseURL: 'https://api.yourapp.com/api',
withCredentials: false,
timeout: 20_000,
});

It also calls await initStorage() once before rendering. See React Native.

2. Wrap your app with providers​

src/App.tsx
import { AuthProvider } from '@rhino-dev/rhino-react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

function App() {
return (
<QueryClientProvider client={queryClient}>
<AuthProvider>
{/* Your routes and components */}
</AuthProvider>
</QueryClientProvider>
);
}

3. Start using hooks​

src/components/PostsList.tsx
import { useModelIndex, useModelStore } from '@rhino-dev/rhino-react';

function PostsList() {
const { data: response, isLoading } = useModelIndex('posts', {
page: 1,
perPage: 20,
sort: '-created_at',
includes: ['user'],
});

const posts = response?.data || [];
const pagination = response?.pagination;

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

return (
<div>
<ul>
{posts.map(post => (
<li key={post.id}>{post.title} — by {post.user?.name}</li>
))}
</ul>
{pagination && (
<p>Page {pagination.currentPage} of {pagination.lastPage} ({pagination.total} total)</p>
)}
</div>
);
}

Feature Map​

Everything the Rhino React client does, in one place. Each row names the hook or export you reach for and links to the page that explains it in full.

1. Configuration​

configureApi(options) sets the base URL and the client's global behavior; <AuthProvider> supplies auth state to the tree.

OptionPurpose
baseURLWhere the API lives
tenancy'path' (default) builds /api/{organization}/{model}; 'subdomain' builds /api/{model} and lets the host carry the org; 'none' builds /api/{model} with no organization at all
routeGroupDefault group for auth URLs (/{group}/auth/*)
routeGroupInDataPathAlso prefix data URLs with the route group: /api/{group}/{model} or /api/{group}/{organization}/{model}
timeout / withCredentialsRequest timeout (default none) and cookies (default true; React Native uses false)
onUnauthorizedCalled on a 401 from any request except login, after the session is cleared and AuthProvider has reset
onForbiddenCalled on a 403; the token is kept
storageA custom synchronous storage adapter (secure store, Electron)

See Utilities, Authentication — Tenancy and Data URLs and Authentication — Group-Aware Auth.

2. Query options​

Every query hook takes the same ModelQueryOptions, mapping one-to-one onto the server's query parameters. Full reference: Querying.

OptionServer parameter
filters?filter[field]=value
sort?sort= (prefix - for descending)
search?search=
includes?include=
fields?fields[model]=
scope?scope= — a server-whitelisted named scope; an unknown name is a 403
computedAttributes?computed_attributes= — opt-in per-record derived values; an object selects attributes that declare parameters
page / perPage?page= / ?per_page=

Every model hook also takes TanStack Query options as its last argument: queryOptions on the query hooks (refetchInterval, enabled, select, staleTime, …) and mutationOptions on the mutation hooks (onSuccess, onError, onMutate, …). They never change the URL or the cache key; enabled can only narrow the hook's own guard. See CRUD Hooks — TanStack Query Options.

3. Response shape​

Pagination comes from response headers, and every list hook parses it for you:

const { data: response } = useModelIndex('posts', { page: 1, perPage: 20 });
response?.data; // the records
response?.pagination; // { currentPage, lastPage, perPage, total }

For accumulating pages ("load more", infinite scroll), useModelInfinite returns data.pages plus fetchNextPage / hasNextPage, driven by the same headers. See Querying — Infinite Scroll.

4. Cache behavior​

Mutations invalidate the queries they affect automatically — a useModelStore('posts') success refreshes every useModelIndex('posts') and useModelInfinite('posts') in the tree. To invalidate or prefetch from outside a component, modelKeys returns the hooks' exact keys and fetchModelIndex / fetchModelShow make their requests (see Utilities — Outside React). Cache keys embed the id you pass, so for models with a server-side route key use the route-key value consistently across index, show and mutations. See CRUD Hooks — Automatic Cache Invalidation.

5. Errors​

StatusMeansWhere it comes from
401Not authenticatedAuth middleware; clears token and user, resets AuthProvider, triggers onUnauthorized — except on login, where login() returns { success: false, status: 401 }
403Not permitted — an action, an ?include=, a scope, or a field you may not writeServer policies
404Missing record, or an organization you don't belong toTenant resolution
422Validation failed, with field-level errorsServer validation

See CRUD Hooks — Error Handling.

6. Platforms​

The same package and the same hooks run on web, React Native (bare or Expo) and Electron. storage adapts to localStorage / AsyncStorage — on React Native, await initStorage() once before rendering — and a custom adapter covers a secure store or Electron's main-process store. See React Native and Desktop / Electron.

7. Sessions​

login() writes the token to storage before it resolves, so the next request is authenticated; LoginResult.token exposes it. useRegister starts a session the same way. A 401 ends it everywhere: storage is cleared and useAuth().isAuthenticated turns false, so navigation that follows isAuthenticated needs no extra wiring. See Authentication — Auth Flow.

8. File uploads​

Pass a FormData to useModelStore or useModelUpdate to upload files: a store is a multipart POST, an update is a multipart POST {url}/{id} with an X-HTTP-Method-Override: PUT header. See CRUD Hooks — File Uploads.


All Available Hooks​

Authentication​

HookDescription
useAuth()Login, logout, token, auth state, setOrganization, setRouteGroup
useRouteGroup()The active route group (populated after a group-aware login or register)
useRegister()Register via an invitation token; starts a session when the backend returns a token
usePasswordRecover()Request a password reset email
useResetPassword()Complete a password reset
useOrganization()Get current organization slug
useOwner()Fetch organization data with relationships
useOrganizationExists()Check if organization slug exists

CRUD Operations​

HookDescription
useModelIndex(model, options?, queryOptions?)List records with filters, sorts, search, scopes, pagination
useModelInfinite(model, options?, queryOptions?)Accumulate pages for "load more" / infinite scroll
useModelShow(model, id, options?, queryOptions?)Fetch single record by ID (or route key)
useModelStore(model, mutationOptions?)Create a new record (JSON or FormData)
useModelUpdate(model, mutationOptions?)Update an existing record (JSON or FormData)
useModelDelete(model, mutationOptions?)Soft delete a record

Soft Deletes​

HookDescription
useModelTrashed(model, options?, queryOptions?)List soft-deleted records
useModelRestore(model, mutationOptions?)Restore a soft-deleted record
useModelForceDelete(model, mutationOptions?)Permanently delete a record

Advanced​

HookDescription
useModelComputedAttributes(model, options?, queryOptions?)Collection-level aggregates from GET /{model}/computed
useModelAudit(model, id, options?, queryOptions?)Fetch audit trail for a record
useNestedOperations(mutationOptions?)Atomic multi-model transactions

Invitations​

HookDescription
useInvitations(status?)List invitations (all, pending, accepted, expired, cancelled)
useInviteUser()Send invitation with role
useResendInvitation()Resend invitation email
useCancelInvitation()Cancel pending invitation
useAcceptInvitation()Accept invitation by token (does not start a session)

Utilities​

ExportDescription
configureApi(options)Configure API base URL, tenancy, route-group data paths, timeout, credentials, storage and handlers
apiPre-configured Axios instance
buildModelUrl(model, options?, target?)The URL a model hook requests, usable outside React
modelKeysThe query keys the model hooks register (.index, .infinite, .show, .computed, .trashed, .audit, .all)
fetchModelIndex / fetchModelShowThe hooks' requests as plain async functions, for fetchQuery / prefetchQuery
storagePlatform-agnostic synchronous storage (localStorage / AsyncStorage)
STORAGE_KEYSEvery storage key the library reads or writes
initStorage()Load the stored session into memory on React Native (await before rendering)
setStorageAdapter(adapter)Swap the storage adapter at runtime
eventsEvent emitter for cross-component communication
extractPaginationFromHeaders(response)Parse pagination from response headers
cn(...classes)CSS class merging utility (clsx + tailwind-merge)
useToast()Toast notification state management

Pagination​

Pagination metadata comes from response headers (not body). All hooks return it automatically:

src/components/PostsList.tsx
const { data: response } = useModelIndex('posts', { page: 1, perPage: 20 });

const posts = response?.data; // Array of records
const pagination = response?.pagination; // { currentPage, lastPage, perPage, total }
src/types.ts
// PaginationMeta type
interface PaginationMeta {
currentPage: number;
lastPage: number;
perPage: number;
total: number;
}

TypeScript Types​

The library exports all types:

src/types.ts
import type {
PaginationMeta,
ModelQueryOptions,
QueryResponse,
LoginResult,
NestedOperation,
Invitation,
InvitationStatus,
ModelQueryHookOptions,
ModelInfiniteQueryHookOptions,
ModelMutationHookOptions,
TenancyMode,
StorageAdapter,
} from '@rhino-dev/rhino-react';

Model interfaces themselves are generated from the server — run php artisan rhino:export-types (Laravel), rails rhino:export_types (Rails) or npx rhino export-types (NestJS) and pass them as generics. See TypeScript.

Documentation map​

PageRead it for
AuthenticationLogin, logout, sessions, organization context, group-aware auth, tenancy and data URLs
CRUD HooksIndex, show, store, update, delete; TanStack Query options, file uploads, errors and cache invalidation
QueryingFilters, sorts, search, scopes, computed attributes, includes, pagination, infinite scroll
Soft DeletesTrashed, restore, force delete
Nested OperationsAtomic multi-model transactions
InvitationsInviting users into organizations
UtilitiesAPI client, storage, events, URL builder, query keys and fetchers for code outside React, toast, audit
TypeScriptGeneric hooks and auto-generated types
Release NotesWhat changed in each version, and how to upgrade
Desktop / ElectronMain/preload/renderer wiring and custom storage
React NativePlatform adapters and mobile setup

The server docs describe what these hooks talk to: Laravel · Rails · NestJS.