Hooks
Every surface in Real Life Stack asks hooks, never the connector. This page lists all 62 hooks the toolkit exports, with the question each answers and what happens when the connector lacks the capability. It is generated from each hook's documentation comment, the same file that feeds Storybook and llms.txt.
The column "without capability" is the important one. Reading hooks answer empty: "no groups", "nobody signed in", "zero comments" are true answers, not errors. Writing hooks fail on the call, not on preparation. Where it says "throws on render", the surface cannot exist without that capability.
- — the hook needs no capability
- empty an empty list or count, never an error
- null
null, the caller decides - value a neutral value (a default, the raw id)
- no-op the call does nothing
- throws on call the surface renders, the action fails
- throws on render the surface cannot exist without the capability
Read items 7
useSurfaceItems() → Item[]The items of this surface, filtered as its head shows.
useActivity(limit?) → {data, supported}What happened in this space recently?
useDraftItem() → Item | nullWhat is someone typing right now that modules should preview?
useModuleFilteredItems(items) → Item[]Exactly what the toolbar in the module head promises: filter plus search text.
useItems(filter?) → {data, isLoading}Which items match this filter, and is the list still arriving?
useItemsWithDraft(filter?) → {data, isLoading}The same list, with the draft being typed already in it as a card.
useItem(id) → {data, isLoading}Does this one item exist, and is "not found" already decided?
Write items 7
useSetDraftItem() → (draft) => voidPublish or discard the running draft.
useItemEditor(options) → {isOpen, mode, submit, remove, …}Open the composer and save an input as an item, including its space.
useCreateItem() → (input, options?) => Promise<Item>Create a new item — optionally directly in a given space ({ group }).
useUpdateItem() → (id, updates) => Promise<Item>Change an existing item.
useDeleteItem() → (id) => Promise<void>Delete an item.
useUnsavedChanges() → {dirty, setDirty} | nullAre there unsaved inputs that leaving must intercept?
useSetUnsavedDirty() → (dirty) => voidReport that something unsaved sits in the form.
Groups and members 10
useGroupVocabulary(fallbackItems?) → {tags, types}Which tags and types exist in the space? The one derivation for filter card, chips and tag suggestions.
usePersonalGroupId() → string | nullWhere does an item belong that is shared with nobody?
useGroups() → {data, isLoading}Which spaces do I see?
useCurrentGroup() → Group | nullWhich space am I in right now?
useCreateGroup() → (name, data?) => PromiseCreate a space.
useUpdateGroup() → (id, patch) => PromiseChange a space's name or data.
useDeleteGroup() → (id) => PromiseDelete a space.
useMembers(groupId) → {data, isLoading}Who belongs to this space, or with null to all of mine?
useInviteMember() → (groupId, userId) => PromiseInvite someone into a space.
useRemoveMember() → (groupId, userId) => PromiseRemove someone from a space.
People 8
useOptionalCurrentUser() → {data, isLoading}Who am I, if anyone at all?
useCurrentUser() → {data, isLoading}Who am I? For surfaces that make no sense without sign-in.
useContacts() → {contacts, addContact, …}Whom do I know, who is waiting for confirmation?
useIncomingEvents() → {current, dismiss, …}Which incoming event is waiting for an answer?
useOpenProfile() → (userId) => voidOpen a person's profile.
useResolvedUsers(ids) → Map<string, User>For which ids do I know a name?
useUserNameResolver() → (userId) => stringA display name for a user id, falling back to the id.
useVerification() → {supported, createChallenge, …}Two people who meet in real life confirm each other.
Relations 9
useCommentCount(itemId) → numberIs there a conversation on this card?
useComments(itemId) → {data, isLoading, canComment, createComment, …}What was said, by whom, and may I reply?
useReplies(itemId, commentId) → {data, isLoading}Which replies does this comment have?
useReactions(itemId) → {data, isLoading, react, canReact}How was reacted, and can I set my reaction?
useReactionUsers(itemId, emojiFilter?) → {data, isLoading}Who reacted with which emoji?
useRelationRecords(filter?) → {data, supported}Which signed relation records match?
useVerifiedRelationRecords(records) → RelationRecord[]Which of those are cryptographically covered?
useVotes(statementId) → {data, isLoading, vote, canVote}How does the resonance stand, and how do I vote?
useVoteUsers(statementId, enabled?) → {data, isLoading}Who voted how?
Permissions and capabilities 3
useOptionalConnector() → DataInterface | nullThe same for surfaces that render without data access too.
useConnector() → DataInterfaceWhich data access applies here?
useItemPermissions(item) → {canEdit, canDelete}May I show the ⋮ menu here?
Host and focus 8
useCreate() → {isComposing, startCreate, patchCreate}Start creating — with suggestion and prefill, never with restriction.
useOptionalCreate() → CreateHostValue | nullThe create host, or null where a surface may stand without one (story, test).
useModuleHost() → {entry, currentSpace, members, groups, items, setCreateAnchor, …}The space context and the items the module host produced for this surface.
useModulePanel() → {current, open, close}Open a panel beside the module. There is only one.
useItemFocus() → {itemId, isEditing, composeType, focusItem, editItem, startCompose, …}Which item is open, is it being edited, is something being created? The focus contract — in the app the URL (UrlFocusProvider from /router), otherwise memory.
useOptionalItemFocus() → ItemFocus | nullThe focus, or null where a surface may stand without one.
useModuleFilter(key, initial) → [value, setValue]A module's own filter value that survives the module switch.
Environment 8
useColorScheme() → "light" | "dark"Is it light or dark right now?
useInitialSync() → {active, loadedGroups, expectedGroups}Is the first fill still running, may "nothing there" be said?
useIsMobile() → booleanNarrower than 768px?
useIsCompact() → booleanBelow 1024px, where the panel becomes a drawer?
useNotifications() → {notifications, badgeCount, …}What is new for me, and how much is unread?
useMarkNotificationsSeen(notifications) → —Advance the seen boundary once per opening.
useRelayStatus() → {state, isConnected, pendingCount}Is this device attached to the relay, is anything unsent waiting?
useI18n() → I18n — {language, locale, t, tDynamic, formatDate, formatTime, formatFullDateTime, formatRelativeTime, setLanguage}In which language and regional locale does this surface speak — and how?
Item properties 2
useItemPresentation(activeGroupId?) → (item) => {group, color, isPrivate}How does this item present itself: which group it comes from, in which colour, and whether it is private?
useItemTags(item) → string[]Which tags does this item carry?
Pairs easily confused
useCurrentUseranduseOptionalCurrentUserdiffer in what happens on a connector without the authentication capability: the first throws on render, the second yieldsnull. While nobody is signed in, both answerdata: null. The same pattern withuseConnector/useOptionalConnector,useItemFocus/useOptionalItemFocus,useCreate/useOptionalCreate.useReactionsalready carries indata[].userIdswhatuseReactionUsersfetches again.useCommentCountis the cheap half ofuseComments.useItemsanduseItemsWithDraftare the same list, the latter with the running draft.useIsMobileanduseIsCompactare the same mechanism with a different threshold: 768px for navigation, 1024px for the panel shape.