API reference
Everything below is exported from both @wherdkit/react and @wherdkit/react-native
(and from @wherdkit/core for the hooks/types).
<WherdProvider>
| Prop | Type | Notes |
|---|---|---|
publicKey | string | Required. Your pk_… key. |
user | WherdUser | Optional identity. { id, email?, name?, userHash?, traits? } |
theme | ThemeTokens | Optional theme override (see Customization). |
activation | { fab?, shake? } | RN only. Override server activation config. |
autoShow | { announcements?, changelog? } | Override auto-show (defaults: announcements on, changelog off). |
Components
| Component | Description |
|---|---|
<MessagesWidget fab?> | Floating launcher + modal with tabs for enabled modules. fab={{ placement?, offset?: { bottom?, side? }, label? }} positions the launcher. |
<MessagesFAB label? placement? offset?> | Just the launcher. Icon circle by default; label → pill. placement = "bottom-right" | "bottom-left", offset = { bottom?, side? }. |
<FeatureBoard /> | Feature requests list + create + upvote. |
<WhatsNew /> | Changelog feed; marks entries read on view. |
Hooks
Every prebuilt component is built on these hooks, so building a fully custom UI
gives you exact parity. All of them must be called inside <WherdProvider>.
useWherd()
The primary hook — reactive widget state plus imperative controls. Use it to open the widget from your own button, read live config, or identify a user.
const wherd = useWherd();
wherd.open("ideas"); // open straight to a tab
Returns:
| Field | Type | Description |
|---|---|---|
ready | boolean | True once the SDK has loaded config + identity. |
isOpen | boolean | Whether the widget panel is open. |
view | WherdView | Active tab: "messages" | "featureRequests" | "changelog". |
config | WidgetConfig | null | Live server-driven config (theme, enabled modules, copy, activation). |
user | WherdUser | null | The identified user, if any. |
verified | boolean | True when the user's identity was HMAC-verified. |
changelogUnread | number | Unread changelog count (for badges). |
messagesUnread | number | Unread admin replies across the user's threads. |
open(view?) | (v?: WherdView) => void | Open the widget, optionally to a tab. |
close() | () => void | Close the widget. |
setView(view) | (v: WherdView) => void | Switch tab without opening/closing. |
identify(user) | (u: WherdUser) => Promise<void> | Attach identity + traits at runtime. |
start({ body, metadata? }) | => Promise<SubmitResult> | Start a new message thread imperatively. |
useComposer()
Drop-in state for starting a new message thread — wire it to an input + button.
const { body, setBody, submit, submitting, submitted, error } = useComposer();
| Field | Type | Description |
|---|---|---|
body | string | Current draft text. |
setBody(v) | (v: string) => void | Update the draft. |
type / setType(t) | MessageType | Category chip: "idea" | "issue" | "other". |
email / setEmail(v) | string | Optional contact email. |
submit() | () => Promise<SubmitResult> | Send it. Validates non-empty first. |
submitting | boolean | In-flight flag for disabling the button. |
submitted | boolean | True after a successful send (show a thank-you). |
queued | boolean | True when saved offline; auto-retries later. |
error | string | null | Validation / network error message. |
reset() | () => void | Clear draft + submitted/error state. |
submit() resolves to SubmitResult = { ok: boolean; queued: boolean; id?: string; error?: string }.
useMessages()
The user's own message threads, with replies and unread counts. Powers the Messages tab; build a custom inbox/conversation UI on it.
const { threads, unreadTotal, loading, reply, markRead } = useMessages();
| Field | Type | Description |
|---|---|---|
threads | MessageThread[] | { id, body, status, createdAt, unread, replies: MessageReply[] }, newest first. |
unreadTotal | number | Unread admin replies across all threads. |
loading | boolean | Initial fetch flag. |
refresh() | () => Promise<void> | Re-fetch threads. |
reply(id, body) | (id, body) => Promise<boolean> | Add the user's reply to a thread. |
markRead(id) | (id) => Promise<void> | Mark a thread's admin replies read. |
useUnreadMessages()
const unread = useUnreadMessages(); // number — reactive
useAnnouncements()
Active, non-dismissed announcement banners.
const { announcements, dismiss } = useAnnouncements();
// announcements: Announcement[] = { id, title, body, severity, publishedAt }
dismiss(id);
useFeatureRequests()
Read, vote on, and create roadmap ideas. Voting is optimistic — the UI updates instantly and reconciles with the server's authoritative count.
const { requests, loading, vote, create } = useFeatureRequests();
// requests: FeatureRequest[] sorted by votes
vote(id, true); // upvote
vote(id, false); // remove vote
create({ title: "Dark mode" }); // suggest a new idea
| Field | Type | Description |
|---|---|---|
requests | FeatureRequest[] | { id, title, body, status, voteCount, hasVoted, createdAt }, vote-sorted. |
loading | boolean | Initial fetch flag. |
refresh() | () => Promise<void> | Re-fetch the list. |
vote(id, next) | (id: string, next: boolean) => Promise<void> | Toggle the caller's vote. |
create(input) | ({ title, body? }) => Promise<FeatureRequest | null> | Suggest a new idea. |
useChangelog()
Render release notes anywhere and track read state.
const { entries, loading, markAllRead } = useChangelog();
// entries: ChangelogEntry[] = { id, title, body, publishedAt, read }
| Field | Type | Description |
|---|---|---|
entries | ChangelogEntry[] | Published entries, newest first, each with a read flag. |
loading | boolean | Initial fetch flag. |
refresh() | () => Promise<void> | Re-fetch entries + unread count. |
markRead(id) | (id: string) => Promise<void> | Mark one entry read. |
markAllRead() | () => Promise<void> | Mark every entry read (clears the badge). |
useUnreadChangelog()
const unread = useUnreadChangelog(); // number — reactive
A cheap, reactive unread count — perfect for a nav bell/badge without mounting the whole changelog.
useResolvedTheme(systemColorScheme?)
const theme = useResolvedTheme("dark");
// theme.colors.primary, theme.radius, theme.fontFamily, theme.colorScheme
Returns concrete ResolvedTheme tokens after merging defaults ← server config
← theme prop. Use it so your custom UI matches the active widget theme.
Pass the platform's current scheme ("light"/"dark") for "system" mode.
Types
WherdUser, WherdView ("messages" | "featureRequests" | "changelog"),
MessageType, MessageThread, MessageReply, Announcement, ThemeTokens,
ResolvedTheme, WidgetConfig, FeatureRequest, ChangelogEntry — all exported.
Behavior notes
- Anonymous by default — a persisted
anonymousIdis generated; callingidentify()promotes that record in place. - Offline queue — messages submitted while offline are persisted and retried automatically on the next app launch.
- Server-driven config — theme, modules, copy, and activation are fetched from your dashboard settings at startup, so you can change them without shipping an app update.