feat(widgets): add server-side prefetch for weather and RSS feed

Prefetch weather and RSS feed data during SSR so widgets render with
content on first paint instead of showing loading states.
This commit is contained in:
Thomas Camlong committed 2026-05-22 11:38:02 +02:00
1 parent 10cedbd906
commit 108b488250
3 files changed
+55

No files matched your search

+4
View File
@@ -11,6 +11,8 @@ import prefetchForApps from "./app/prefetch";
import prefetchForBookmarks from "./bookmarks/prefetch";
import type { Prefetch, WidgetOptionsRecordOf } from "./definition";
import type { inferOptionsFromCreator } from "./options";
import prefetchRssFeedsAsync from "./rssFeed/prefetch";
import prefetchWeatherAsync from "./weather/prefetch";
const cachedGetServerSettingsAsync = cache(getServerSettingsAsync);
@@ -19,6 +21,8 @@ const prefetchCallbacks: Partial<{
}> = {
bookmarks: prefetchForBookmarks,
app: prefetchForApps,
weather: prefetchWeatherAsync,
rssFeed: prefetchRssFeedsAsync,
};
export const prefetchForKindAsync = async <TKind extends WidgetKind>(
+22
View File
@@ -0,0 +1,22 @@
import { api, trpc } from "@homarr/api/server";
import { createLogger } from "@homarr/core/infrastructure/logs";
import type { Prefetch } from "../definition";
const logger = createLogger({ module: "rssFeedWidgetPrefetch" });
const prefetchRssFeedsAsync: Prefetch<"rssFeed"> = async (queryClient, items) => {
for (const item of items) {
const input = {
urls: item.options.feedUrls,
maximumAmountPosts: item.options.maximumAmountPosts,
};
const data = await api.widget.rssFeed.getFeeds(input);
queryClient.setQueryData(trpc.widget.rssFeed.getFeeds.queryKey(input), data);
}
logger.info("Successfully prefetched RSS feeds", { count: items.length });
};
export default prefetchRssFeedsAsync;
+29
View File
@@ -0,0 +1,29 @@
import { api, trpc } from "@homarr/api/server";
import { createLogger } from "@homarr/core/infrastructure/logs";
import type { Prefetch } from "../definition";
const logger = createLogger({ module: "weatherWidgetPrefetch" });
const prefetchWeatherAsync: Prefetch<"weather"> = async (queryClient, items) => {
const locations = items.map((item) => ({
latitude: item.options.location.latitude,
longitude: item.options.location.longitude,
}));
const uniqueLocations = locations.filter(
(loc, idx, arr) =>
arr.findIndex((other) => other.latitude === loc.latitude && other.longitude === loc.longitude) === idx,
);
await Promise.all(
uniqueLocations.map(async (input) => {
const data = await api.widget.weather.atLocation(input);
queryClient.setQueryData(trpc.widget.weather.atLocation.queryKey(input), data);
}),
);
logger.info("Successfully prefetched weather data", { count: uniqueLocations.length });
};
export default prefetchWeatherAsync;