DEV Community

Cover image for Production Level Context with Next.js (Typescript) 🔥
Subham
Subham

Posted on • Edited on

Production Level Context with Next.js (Typescript) 🔥

🔥Connect: https://www.subham.online

🔥Twitter: https://twitter.com/TheSubhamMaity


Setup

First, let's create a file for defining the context (let's call it app-context.ts):

import React from "react";

export interface ContextState {
  name: string;
}

export interface ContextDispatch {
  setName: React.Dispatch<React.SetStateAction<string>>;
}

type ContextProps = ContextState & ContextDispatch;

const defaultState: ContextState = {
  name: "Hello",
};

const defaultDispatch: ContextDispatch = {
  setName: () => {},
};

const defaultContext: ContextProps = {
  ...defaultState,
  ...defaultDispatch,
};

const AppContext = React.createContext<ContextProps>(defaultContext);

export default AppContext;
Enter fullscreen mode Exit fullscreen mode

Now, let's create a file for the provider component (let's call it app-context-provider.tsx):

"use client";
import React, { useState } from "react";
import AppContext, { ContextState, ContextDispatch } from "./appContext";

interface AppProviderProps {
  children: React.ReactNode;
}

const AppProvider: React.FC<AppProviderProps> = ({ children }) => {
  const [name, setName] = useState<string>("Hello");

  const contextState: ContextState = {
    name,
  };

  const contextDispatch: ContextDispatch = {
    setName,
  };

  return (
    <AppContext.Provider value={{ ...contextState, ...contextDispatch }}>
      {children}
    </AppContext.Provider>
  );
};

export default AppProvider;
Enter fullscreen mode Exit fullscreen mode

Finally, let's create a custom hook for using the context (you can put this in a separate file or in the index.ts file):

import { useContext } from "react";
import AppContext from "./appContext";

export function useAppContext() {
  return useContext(AppContext);
}
Enter fullscreen mode Exit fullscreen mode

Now you can use it in your application like this:

Wrap your app or a part of it with the AppProvider:
layout.tsx

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body className={`${inter.className} bg`}>
        <AppProvider>{children}</AppProvider>
      </body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

Use the context in your components:

a-component.tsx

"use client";
import React from "react";
import { useAppContext } from "@/context";

const AComponent = () => {
  const { setName } = useAppContext();
  return (
    <div>
      <button
        className="default-button"
        onClick={() => {
          setName("Subham Maity");
        }}
      >
        Change The Name
      </button>
    </div>
  );
};

export default AComponent;
Enter fullscreen mode Exit fullscreen mode

b-component.tsx

"use client";
import React from "react";
import { useAppContext } from "@/context";

const BComponent = () => {
  const { name } = useAppContext();
  return (
    <div>
      <p>{name}</p>
    </div>
  );
};

export default BComponent;
Enter fullscreen mode Exit fullscreen mode

This structure provides better type safety, separates concerns, and is more scalable. It's easier to add new state variables and update functions as your app grows.

Top comments (0)