Docs
Vite
A single-page app: the widget is one line in index.html, and the hooks (or the framework-free core, for Vue and Svelte) give you the data.
Create a project
Sign in, create a project, and note its slug.
Add the widget
npx shipnoted initIt adds one line to
index.html, before</body>:index.html
<script src="https://shipnoted.com/widget.js" data-project="your-slug" async></script>
React hooks#
npm i @shipnoted/reactsrc/Changelog.tsx
import { ShipnotedProvider, useChangelog, useFeedback } from "@shipnoted/react"
function Page() {
const { releases, loading, unread, markSeen } = useChangelog({ limit: 10 })
const { items, vote, hasVoted } = useFeedback({ status: "open", sort: "top" })
if (loading) return <p>Loading…</p>
return (
<>
<h1>What's new {unread > 0 && <b>({unread})</b>}</h1>
{releases.map((r) => <article key={r.id}><h2>{r.title}</h2><p>{r.summary}</p></article>)}
<h1>Vote</h1>
{items.map((f) => <button key={f.id} aria-pressed={hasVoted(f.id)} onClick={() => vote(f.id)}>▲ {f.votes} {f.title}</button>)}
</>
)
}
export default () => <ShipnotedProvider project="your-slug" baseUrl="https://shipnoted.com"><Page /></ShipnotedProvider>Vue, Svelte and others#
The data logic does not depend on React. @shipnoted/react/core exports plain stores with subscribe and getSnapshot, which every framework can bind to.
Svelte
import { createPublicClient, createChangelog } from "@shipnoted/react/core"
const log = createChangelog(createPublicClient({ project: "your-slug", baseUrl: "https://shipnoted.com" }), { limit: 10 })
log.refresh()
// a Svelte store is any object with subscribe(fn); wrap getSnapshot:
export const changelog = { subscribe(fn) { fn(log.getSnapshot()); return log.subscribe(() => fn(log.getSnapshot())) } }Verified customers#
A browser-only app cannot sign tokens safely: the identify secret has to stay on a server. Add one small endpoint to your backend (any language: it is a standard HS256 JWT), then hand the token to the widget or the provider:
const { token } = await (await fetch("/api/shipnoted-token")).json()
window.Shipnoted?.identify(token) // the widget
// <ShipnotedProvider userToken={token} …> // the hooks