How React Context is used in this blog

2025-04-12

An exploration of how React Context is implemented in this blog for configuration management, with reflections on its advantages, drawbacks, and potential future applications for state management

How React Context is used in this blog

I recently came across React Context, and it was a stark reminder of how much the React ecosystem has evolved since I last actively developed with it. I've heard about it but did not really understand it well. Back in 2020, I transitioned from a backend developer role to become a Site Reliability Engineer (SRE), and my React knowledge quickly became obsolete. State management was primarily done through Redux or similar external libraries, prop drilling was a common pain point, and Context API was still in its infancy.

Fast forward to today, and React's Context API has matured into a powerful tool for state management. When I decided to rebuild my personal blog with Remix, I wanted to catch up on modern React practices, and implementing Context seemed like a perfect opportunity to refresh my knowledge.

What is React Context?

Before diving into my implementation, let's briefly discuss what React Context is. React Context provides a way to pass data through the component tree without having to pass props down manually at every level. It's designed to share data that can be considered "global" for a tree of React components, such as:

  • Current authenticated user
  • Theme preferences
  • Language preferences
  • Site-wide configuration

Context solves the "prop drilling" problem, where data needs to be passed through multiple layers of components, even when intermediate components don't need that data.

How I implemented Context in my blog

In this blog, I use Context primarily for storing and accessing site-wide configuration. Let's look at how it's structured:

1. Creating the Context

I defined a SiteConfigContext to hold all site-wide configuration:

// Define the site configuration type
export interface SiteConfig {
  baseUrl: string;
  siteName: string;
  description: string;
  keywords: string;
  ogImage: {
    src: string;
    width: number;
    height: number;
  };
  author: {
    name: string;
    position: string;
    url: string;
  };
}

// Default configuration values
export const defaultSiteConfig: SiteConfig = {
  baseUrl: "https://kenwagatsuma.com",
  siteName: "Ken Wagatsuma's Homepage",
  // ...other config values
};

// Create the context with default values
const SiteConfigContext = createContext<SiteConfig>(defaultSiteConfig);

2. Creating the Provider

I then created a provider component to wrap around parts of my application that need access to this config:

export const SiteConfigProvider = ({ children }: { children: ReactNode }) => {
  return (
    <SiteConfigContext.Provider value={defaultSiteConfig}>
      {children}
    </SiteConfigContext.Provider>
  );
};

3. Creating a Custom Hook

To make consumption of the context easier throughout my application, I created a custom hook:

export const useSiteConfig = () => useContext(SiteConfigContext);

4. Using the Context in Components

With this setup, I can now easily access my site configuration anywhere in my component tree:

const Author: React.FC = () => {
  const { author } = useSiteConfig();
  const { name, position, url } = author;

  return (
    <Card className={classes.authorContainer}>
      <p className={classes.authorMetadataName}>
        <a href={url} className={classes.authorMetadataNameLink}>
          {name}
        </a>
      </p>
      <p className={classes.authorMetadataPosition}>
        {position}
      </p>
    </Card>
  );
};

5. Integrating with Remix

In a Remix application, I integrated the Context provider in my root layout:

export function Layout({ children }: { children: React.ReactNode }) {
  const siteConfig = useSiteConfig();

  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta name="author" content={siteConfig.author.name} />
        <Meta />
        <Links />
      </head>
      <body>
        {children}
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}

export default function App() {
  return (
    <SiteConfigProvider>
      <Outlet />
    </SiteConfigProvider>
  );
}

Integrating with Remix data handling

One interesting aspect of using Context with Remix is the interplay between Context and Remix's data loading strategy. Remix uses loaders and actions to fetch and mutate data on the server, which can then be accessed via hooks like useLoaderData().

For example, in my blog post routes, I combine Remix's data loading with the Context configuration:

export const meta: MetaFunction<typeof loader> = ({ data, location }) => {
  if (!data) {
    return defaultMetaFunction(location);
  }

  const { post, postType } = data;
  // Using the default context value for server-side rendering
  const siteConfig = useSiteConfig();

  const title = `${post.title} - ${siteConfig.siteName}`;
  // More meta tags using both loader data and context values
};

This hybrid approach allows me to:

  1. Load dynamic data from the server via Remix loaders
  2. Access application-wide configuration via Context
  3. Combine them for specific use cases like generating meta tags

Conclusion

Implementing React Context in my personal blog has been a valuable learning experience. It's helped me catch up with modern React development after years of focusing on backend and infrastructure work. While my current usage is relatively simple—primarily for site configuration—I see many opportunities to expand its use as my blog evolves.

Ken Wagatsuma

Programmer. Generalist. Open-minded amateur.