What Is the Role of SWR in the Plane Frontend?
SWR (stale-while-revalidate) serves as the centralized data-fetching backbone for Plane’s React frontend, providing automatic caching, background revalidation, and error retry logic through the useSWR hook and shared configurations in packages/constants/src/swr.ts.
Plane leverages SWR to unify server-state management across its open-source project management interface. By abstracting API logic into reusable hooks and centralized configuration files, the codebase eliminates redundant network requests while ensuring the UI remains responsive and immediately populated with cached data.
Centralized Configuration Architecture
All SWR instances in Plane inherit from shared defaults defined in packages/constants/src/swr.ts. This centralization ensures consistent caching behavior and network policies across the entire application.
Default vs. Web Configurations
The repository exports two distinct configuration objects that cater to different data freshness requirements:
-
DEFAULT_SWR_CONFIG– Optimized for internal API calls where background refreshes are unnecessary. This configuration minimizes network traffic by disabling automatic revalidation when the tab regains focus or when the cache is stale. -
WEB_SWR_CONFIG– Designed for public-facing components such as workspace lists and notification feeds where data must remain fresh.
// packages/constants/src/swr.ts
export const DEFAULT_SWR_CONFIG = {
refreshWhenHidden: false,
revalidateIfStale: false,
revalidateOnFocus: false,
revalidateOnMount: true,
refreshInterval: 600_000,
errorRetryCount: 3,
};
By importing these constants wherever useSWR is instantiated, Plane maintains a single source of truth for data-fetching behavior, according to the Plane source code.
Data Flow and Caching Mechanisms
When components wrap API calls with useSWR, Plane guarantees three core behaviors that optimize the user experience:
-
Instant Caching – The first successful response is stored locally, allowing subsequent renders to read from cache immediately without loading states.
-
Stale-While-Revalidate Updates – The UI displays cached data instantly while a background request fetches fresh data. Once the new data resolves, React automatically re-renders the component with updated values.
-
Robust Error Handling – The shared
errorRetryCountparameter prevents infinite refetch loops by limiting retries to three attempts before surfacing the error to the UI.
Integration Points Across the UI
SWR is woven throughout Plane’s component hierarchy, from authentication wrappers to infinite-scrolling integrations.
Authentication and User State
The apps/web/core/lib/wrappers/authentication-wrapper.tsx file demonstrates how SWR bootstraps the user session. It fetches current user data using a stable key that persists across route changes:
// apps/web/core/lib/wrappers/authentication-wrapper.tsx
const { isLoading: isUserSWRLoading } = useSWR(
"USER_INFORMATION",
async () => await fetchCurrentUser(),
WEB_SWR_CONFIG
);
This pattern ensures that user information remains available globally without prop drilling, while WEB_SWR_CONFIG keeps the session data fresh throughout the application lifecycle.
Workspace and Project Management
Domain-specific wrappers like workspace-wrapper.tsx and project-wrapper.tsx utilize SWR to fetch hierarchical data. Components such as views-list.tsx and members-list.tsx follow the same pattern, passing workspace slugs as cache keys to isolate data per context:
import useSWR from "swr";
import { DEFAULT_SWR_CONFIG } from "@plane/constants";
const fetchUserProjects = async (workspaceSlug: string) => {
const resp = await fetch(`/api/workspaces/${workspaceSlug}/projects`);
return resp.json();
};
export const ProjectList = ({ workspaceSlug }: { workspaceSlug: string }) => {
const { data, error, isLoading } = useSWR(
[`PROJECTS_${workspaceSlug}`],
() => fetchUserProjects(workspaceSlug),
DEFAULT_SWR_CONFIG
);
if (isLoading) return <Spinner />;
if (error) return <ErrorMsg>{error.message}</ErrorMsg>;
return (
<ul>
{data?.map((proj: any) => (
<li key={proj.id}>{proj.name}</li>
))}
</ul>
);
};
Infinite Scrolling with useSWRInfinite
For paginated data such as GitHub repository listings, Plane imports useSWRInfinite from swr/infinite. As implemented in apps/web/core/components/integration/github/select-repository.tsx, the hook manages cursor-based pagination by checking previousPageData to determine if additional pages exist:
import useSWRInfinite from "swr/infinite";
const getKey = (pageIndex: number, previousPageData: any) => {
if (previousPageData && !previousPageData.length) return null;
return `/api/github/repos?page=${pageIndex + 1}`;
};
const GithubRepoSelect = () => {
const { data, size, setSize, isValidating } = useSWRInfinite(getKey, fetcher);
const repos = data?.flat() ?? [];
return (
<Select
options={repos}
onScrollEnd={() => setSize(size + 1)}
loading={isValidating}
/>
);
};
Performance Optimization Strategies
Plane fine-tunes SWR’s behavior to balance data freshness against network congestion. By setting refreshWhenHidden and revalidateOnFocus to false in DEFAULT_SWR_CONFIG, the application avoids redundant API calls when users switch tabs or return to inactive windows.
For critical real-time features like notification badges, components switch to WEB_SWR_CONFIG, enabling background updates:
import useSWR from "swr";
import { WEB_SWR_CONFIG } from "@plane/constants";
const fetchNotifications = async () => {
const resp = await fetch("/api/notifications");
return resp.json();
};
export const NotificationBell = () => {
const { data: notifications, isLoading } = useSWR(
"USER_NOTIFICATIONS",
fetchNotifications,
WEB_SWR_CONFIG
);
return (
<Badge count={notifications?.length ?? 0}>
{isLoading ? <Spinner size="sm" /> : <BellIcon />}
</Badge>
);
};
The ten-minute refreshInterval (600,000ms) provides a safety net for long-lived sessions without overwhelming the backend infrastructure.
Summary
- SWR provides the data-fetching backbone for Plane’s React frontend through the
useSWRanduseSWRInfinitehooks. - Centralized configurations in
packages/constants/src/swr.tsenforce consistent caching and retry policies across the application. DEFAULT_SWR_CONFIGoptimizes for performance by disabling background refreshes, whileWEB_SWR_CONFIGensures real-time updates for critical UI elements.- Authentication wrappers, workspace loaders, and paginated GitHub integrations all rely on SWR’s automatic revalidation and error handling.
- Strategic use of cache keys and configuration flags minimizes network usage while maintaining data freshness.
Frequently Asked Questions
How does Plane configure SWR for different data freshness requirements?
Plane maintains two exported configuration objects in packages/constants/src/swr.ts. DEFAULT_SWR_CONFIG disables background revalidation (revalidateIfStale: false, revalidateOnFocus: false) for standard API calls, while WEB_SWR_CONFIG enables real-time updates for public-facing components like notification feeds and workspace lists.
Where does Plane handle user authentication state with SWR?
The apps/web/core/lib/wrappers/authentication-wrapper.tsx file fetches the current user session using useSWR with the cache key "USER_INFORMATION" and the WEB_SWR_CONFIG configuration. This ensures user data remains fresh across navigation while providing a global loading state for authentication checks.
How does Plane implement pagination for external integrations like GitHub?
Plane utilizes useSWRInfinite from the swr package in components such as apps/web/core/components/integration/github/select-repository.tsx. The implementation defines a getKey function that checks previousPageData to terminate pagination when no more results exist, while setSize triggers additional page fetches during scroll events.
What error handling mechanisms does SWR provide in the Plane frontend?
Through the centralized errorRetryCount: 3 setting in DEFAULT_SWR_CONFIG, SWR automatically retries failed requests up to three times before returning an error to the component. This prevents infinite refetch loops and provides consistent error states across the UI without manual try-catch blocks in every component.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →