CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-swr

React Hooks library for remote data fetching with stale-while-revalidate caching strategy

Pending
Overview
Eval results
Files

immutable-data.mddocs/

Immutable Data

The useSWRImmutable hook is designed for static data that doesn't require revalidation, providing the same API as useSWR but with all revalidation options disabled.

Capabilities

useSWRImmutable Hook

Hook for static data that doesn't require revalidation, with all revalidation options disabled.

/**
 * Hook for static data that doesn't require revalidation
 * @param key - Unique identifier for the request
 * @param fetcher - Function that fetches the data, or null to disable fetching
 * @param config - Configuration options (revalidation options are disabled)
 * @returns SWRResponse object identical to useSWR
 */
function useSWRImmutable<Data = any, Error = any>(
  key: Key,
  fetcher?: Fetcher<Data, Key> | null,
  config?: SWRConfiguration<Data, Error>
): SWRResponse<Data, Error>;

Usage Examples:

import useSWRImmutable from "swr/immutable";

// Static configuration data
const { data: config } = useSWRImmutable("/api/config", fetcher);

// User profile (changes infrequently)
const { data: profile } = useSWRImmutable(
  userId ? `/api/users/${userId}/profile` : null,
  fetcher
);

// Application constants
const { data: constants } = useSWRImmutable("/api/constants", fetcher);

// Translation data
const { data: translations } = useSWRImmutable(
  `/api/i18n/${locale}`,
  fetcher
);

// One-time data fetch
const { data: report } = useSWRImmutable(
  `/api/reports/${reportId}`,
  fetcher
);

Behavior Differences

useSWRImmutable automatically disables all revalidation options:

// These options are automatically set to false:
const defaultConfig = {
  revalidateOnFocus: false,
  revalidateIfStale: false,
  revalidateOnReconnect: false,
  // All other options work normally
};

Comparison with useSWR:

// Regular useSWR - will revalidate on focus, reconnect, etc.
const { data: dynamicData } = useSWR("/api/dynamic", fetcher);

// useSWRImmutable - will never revalidate automatically
const { data: staticData } = useSWRImmutable("/api/static", fetcher);

// Equivalent to useSWR with manual revalidation disabled
const { data: manualStatic } = useSWR("/api/static", fetcher, {
  revalidateOnFocus: false,
  revalidateIfStale: false,
  revalidateOnReconnect: false,
});

When to Use useSWRImmutable

Perfect for:

// Application configuration
const { data: appConfig } = useSWRImmutable("/api/app-config", fetcher);

// Feature flags (when they don't change during session)
const { data: features } = useSWRImmutable("/api/feature-flags", fetcher);

// Static content (documentation, help text)
const { data: helpContent } = useSWRImmutable(
  `/api/help/${section}`,
  fetcher
);

// Cached computed results
const { data: expensiveComputation } = useSWRImmutable(
  ["compute", complexParams],
  ([, params]) => performExpensiveComputation(params)
);

// Historical data (doesn't change)
const { data: historicalData } = useSWRImmutable(
  `/api/history/${date}`,
  fetcher
);

// Reference data (currencies, countries, etc.)
const { data: currencies } = useSWRImmutable("/api/currencies", fetcher);
const { data: countries } = useSWRImmutable("/api/countries", fetcher);

Manual Revalidation

Even with useSWRImmutable, you can still trigger manual revalidation:

function StaticDataComponent() {
  const { data, mutate } = useSWRImmutable("/api/config", fetcher);
  
  // Manual refresh (still works)
  const handleRefresh = () => {
    mutate(); // This will revalidate even with useSWRImmutable
  };
  
  return (
    <div>
      <div>Config: {JSON.stringify(data)}</div>
      <button onClick={handleRefresh}>Force Refresh</button>
    </div>
  );
}

// Global manual revalidation
import { mutate } from "swr";

const refreshStaticData = () => {
  mutate("/api/config"); // Works even if using useSWRImmutable
};

Advanced Patterns

Conditional Immutability:

function DataComponent({ isStatic }: { isStatic: boolean }) {
  // Choose hook based on data nature
  const hook = isStatic ? useSWRImmutable : useSWR;
  const { data, error } = hook("/api/data", fetcher);
  
  return <div>{data ? JSON.stringify(data) : "Loading..."}</div>;
}

Cache Warming:

function App() {
  // Pre-load static data that will be needed throughout the app
  useSWRImmutable("/api/config", fetcher);
  useSWRImmutable("/api/user-permissions", fetcher);
  useSWRImmutable("/api/feature-flags", fetcher);
  
  return <Router />;
}

// Later components can access the cached data instantly
function FeatureComponent() {
  const { data: features } = useSWRImmutable("/api/feature-flags", fetcher);
  // This will return cached data immediately, no loading state
  
  return features?.newFeature ? <NewFeature /> : <OldFeature />;
}

Locale-Specific Data:

function LocalizedApp() {
  const [locale, setLocale] = useState("en");
  
  // Translation data is immutable per locale
  const { data: translations } = useSWRImmutable(
    `/api/i18n/${locale}`,
    fetcher
  );
  
  // When locale changes, a new request is made but data for each locale
  // is cached permanently (until page refresh)
  
  return (
    <div>
      <select value={locale} onChange={(e) => setLocale(e.target.value)}>
        <option value="en">English</option>
        <option value="es">Spanish</option>
        <option value="fr">French</option>
      </select>
      
      <div>{translations ? <TranslatedContent translations={translations} /> : "Loading..."}</div>
    </div>
  );
}

Static Resource Loading:

// Hook for loading static resources
function useStaticResource<T>(path: string): T | undefined {
  const { data } = useSWRImmutable(
    path,
    async (path: string) => {
      const response = await fetch(path);
      if (!response.ok) {
        throw new Error(`Failed to load ${path}`);
      }
      return response.json();
    }
  );
  
  return data;
}

// Usage
function DocumentationPage() {
  const schema = useStaticResource<JsonSchema>("/schemas/api.json");
  const examples = useStaticResource<Examples>("/examples/api-examples.json");
  
  if (!schema || !examples) {
    return <div>Loading documentation...</div>;
  }
  
  return <ApiDocumentation schema={schema} examples={examples} />;
}

Performance Optimization:

// Use useSWRImmutable for expensive computations that don't change
function ExpensiveChart({ dataParams }: { dataParams: DataParams }) {
  const { data: processedData } = useSWRImmutable(
    ["processed-data", dataParams],
    ([, params]) => {
      // This expensive computation will only run once per unique params
      return processLargeDataset(params);
    }
  );
  
  return <Chart data={processedData} />;
}

// Heavy external API calls for static data
function CountryInfo({ countryCode }: { countryCode: string }) {
  const { data: countryDetails } = useSWRImmutable(
    `country-${countryCode}`,
    () => fetchCountryFromExternalAPI(countryCode), // Expensive external call
    {
      // Even though it's immutable, we might want error retry
      shouldRetryOnError: true,
      errorRetryInterval: 1000,
    }
  );
  
  return <div>{countryDetails?.name}</div>;
}

Best Practices

When to choose useSWRImmutable over useSWR:

  1. Static Configuration: App config, feature flags, constants
  2. Reference Data: Countries, currencies, time zones
  3. Historical Data: Past reports, archived content
  4. Heavy Computations: Results that are expensive to recalculate
  5. One-time Fetches: Data that definitely won't change during session
  6. Static Content: Help docs, terms of service, static pages

When to stick with useSWR:

  1. User Data: Profiles, preferences (even if updated infrequently)
  2. Live Data: Anything that might change during user session
  3. Collaborative Data: Content that other users might modify
  4. Time-Sensitive Data: Even if updates are rare
  5. Error Recovery: When you need automatic revalidation on reconnect

Performance Considerations:

// Good: Static data that never changes
const { data: constants } = useSWRImmutable("/api/constants", fetcher);

// Good: Heavy computation with stable inputs
const { data: result } = useSWRImmutable(
  ["heavy-calc", stableParams],
  computeExpensiveResult
);

// Avoid: Data that might change (use useSWR instead)
// const { data: userNotifications } = useSWRImmutable("/api/notifications", fetcher);

// Avoid: External APIs that might return different data
// const { data: weather } = useSWRImmutable("/api/weather", fetcher);

Install with Tessl CLI

npx tessl i tessl/npm-swr

docs

cache-management.md

core-data-fetching.md

global-configuration.md

immutable-data.md

index.md

infinite-loading.md

mutations.md

subscriptions.md

tile.json