Common Patterns
Idiomatic ways to combine Cloudillo APIs when building apps. Each pattern shows the minimal Cloudillo-specific code — wrap in your own components as needed. For full API details, see the linked reference pages.
Data loading
Paginated lists (infinite scroll)
Cloudillo list APIs use cursor-based pagination. The useInfiniteScroll hook from @cloudillo/react manages cursor tracking, accumulates items across pages, and triggers loading via IntersectionObserver when a sentinel element becomes visible.
import { useInfiniteScroll } from '@cloudillo/react'
const { items, isLoading, hasMore, sentinelRef, prepend } = useInfiniteScroll({
fetchPage: async (cursor, limit) => {
const result = await api.actions.listPaginated({ type: 'POST', cursor, limit })
return {
items: result.data,
nextCursor: result.cursorPagination?.nextCursor ?? null,
hasMore: result.cursorPagination?.hasMore ?? false
}
},
pageSize: 20,
deps: [filterType] // resets when dependencies change
})Attach sentinelRef to a <div> at the bottom of your list — it automatically triggers loadMore when scrolled into view. Use prepend() to insert real-time updates at the top without resetting the list.
See: @cloudillo/react | Actions API
Profile search
The profiles API supports text search. Combine with your own debounce logic to reduce API calls:
const results = await api.profiles.list({ q: searchQuery, limit: 10 })See: Profiles API
Real-time collaboration
Collaborative editing (CRDT)
The useCloudilloEditor hook sets up a Yjs document with WebSocket sync. It returns a Y.Doc and a WebsocketProvider — bind these to any Yjs-compatible editor (Quill, ProseMirror, CodeMirror, BlockNote). Wait for synced before initializing the editor binding.
import { useCloudilloEditor } from '@cloudillo/react'
const { yDoc, provider, synced, error, ownerTag, fileId } = useCloudilloEditor('my-app')
// Once synced, bind to your editor:
const yText = yDoc.getText('content')
const binding = new QuillBinding(yText, quill, provider.awareness)The error field captures connection errors (e.g., 440x codes from the CRDT server) so you can show appropriate UI.
See: CRDT (Collaborative Editing) | @cloudillo/react
Presence awareness
The provider from useCloudilloEditor includes a Yjs awareness instance. Subscribe to it to show who is currently viewing or editing:
provider.awareness.on('change', () => {
const states = provider.awareness.getStates()
// Each state contains { user: { name, profilePic, ... } }
// Filter out own clientID: provider.awareness.clientID
})See: CRDT (Collaborative Editing)
Social features
Connection requests
Cloudillo connections are bidirectional. Send a request by creating a CONN action, and accept incoming requests with accept:
// Send a connection request
await api.actions.create({
type: 'CONN',
subject: profile.idTag,
content: 'Would love to connect!'
})
// Accept an incoming request
await api.actions.accept(actionId)Check profile.connected for the current connection state:
true— connected'R'— request pending- absent — not connected
See: Actions API
Role-based access
Community roles follow a numeric hierarchy defined in ROLE_LEVELS. Use useAuth() to get the current user’s roles and compare:
import { ROLE_LEVELS, type CommunityRole } from '@cloudillo/types'
function hasRole(userRoles: string[] | undefined, required: CommunityRole): boolean {
if (!userRoles?.length) return false
const level = Math.max(...userRoles.map(r => ROLE_LEVELS[r as CommunityRole] ?? 0))
return level >= ROLE_LEVELS[required]
}
// Usage: hasRole(auth?.roles, 'moderator')
Role hierarchy: public < follower < supporter < contributor < moderator < leader.
See: @cloudillo/react | @cloudillo/types
File management
Uploading files
Use api.files.uploadBlob with a preset name. The preset determines what variants (thumbnail, standard definition) are automatically generated server-side:
const result = await api.files.uploadBlob(
'gallery', // preset: generates thumbnail + SD variants
file.name,
file,
file.type
)
// result: { fileId, variantId }
See: Files API
General tips
- Use
useToast()from@cloudillo/reactfor user feedback after API calls — see components reference - Wrap route-level components in an error boundary for graceful failure handling — see error handling
- For reactions and toggles, use optimistic UI updates: save previous state, update immediately, rollback in
catch - All list APIs support cursor pagination — prefer
useInfiniteScrollover manual fetch loops - Check
apiis not null (user is authenticated) before making API calls - CRDT and RTDB serve different use cases: CRDT for rich document editing, RTDB for structured data collections — see RTDB and CRDT
See also
- @cloudillo/react — React hooks and components
- @cloudillo/core — Core SDK
- Components reference — All UI components
- Error handling — Error codes and strategies
- CRDT (Collaborative Editing) — Full CRDT guide
- RTDB (Real-Time Database) — Structured data collections