Skip to content

feat(client): prefetch and hydrate in-app feeds for SSR#1007

Open
gu-stav wants to merge 1 commit into
knocklabs:mainfrom
gu-stav:feat/feed-initial-data-ssr-hydration
Open

feat(client): prefetch and hydrate in-app feeds for SSR#1007
gu-stav wants to merge 1 commit into
knocklabs:mainfrom
gu-stav:feat/feed-initial-data-ssr-hydration

Conversation

@gu-stav
Copy link
Copy Markdown

@gu-stav gu-stav commented Jun 1, 2026

Description

The in-app feed is usually the first thing people look at — the notification list, the unread badge. It's also the one part of the page that can't render on the server. Everything around it streams down with the initial HTML, then the feed sits as a skeleton until the client boots, authenticates, and makes its own round-trip to Knock. On a cold load that's the slowest pixel on the screen, and it's the one users came for.

The reason it's stuck client-side is that there's no supported way to hand a server-fetched feed to the client. You can fetch it on the server, but the SDK has no entry point to render from it — so teams either live with the skeleton flash or call the REST API directly and rebuild the response shape by hand, which quietly drifts from what the client expects and breaks the moment realtime reconciles.

This closes that gap. FeedClient.prefetch() fetches a feed on the server without spinning up a feed instance or a socket, and a new initialData feed option seeds the store from that response — or from a still-pending promise, so a loader can kick off the request without awaiting it and let the framework stream the result down. The feed paints with real content on first render. Because prefetch sends the exact same request the client would have, there's no flash when the live connection takes over.

It's modeled on TanStack Query's prefetchQuery / initialData / hydrate, so it lands on a mental model most people already carry. Fully opt-in and backwards compatible — leave the option off and nothing changes; the feed still connects and reconciles exactly as before.

Checklist

  • Tests have been added for new features or major refactors to existing features.

The in-app feed only renders after the client hydrates and fetches it
itself, so the notification surface flashes a skeleton on first paint
even when the server already had the data.

Add FeedClient.prefetch() to fetch a feed server-side and an initialData
feed option (a FeedResponse or a promise of one) to seed the store from
it, so frameworks can stream the feed down with the initial render.
Modeled on TanStack Query's prefetch/initialData/hydrate.
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Jun 1, 2026

🦋 Changeset detected

Latest commit: 048cd17

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 11 packages
Name Type
@knocklabs/client Minor
client-example Patch
guide-example Patch
@knocklabs/expo Patch
@knocklabs/react-core Patch
@knocklabs/react-native Patch
@knocklabs/react Patch
ms-teams-connect-example Patch
nextjs-app-dir-example Patch
slack-connect-example Patch
slack-kit-example Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 1, 2026

@gu-stav is attempting to deploy a commit to the Knock Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 048cd17. Configure here.

method: "GET",
url: `/v1/users/${this.knock.userId}/feeds/${this.feedId}`,
params: queryParams,
params: buildFeedRequestParams(this.defaultOptions, options),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fetch loading hides synced data

Medium Severity

When initialData is a resolved FeedResponse, hydration sets the store to ready with items, but a first-page fetch() still sets networkStatus to loading. UIs that hide feed items while status is loading can drop server-seeded content until the request completes, which conflicts with rendering hydrated data on first paint.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 048cd17. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant