Sharing data across client & server components?

I am trying to understand how I am supposed to share data across my components. What I am trying to do is have a auth_token saved in memory that I can share across my application. Right now It feels like I have two options

1) A context provider
2) prop drilling

Mixing both of these options seems to get me what I want. My issues is that my gut tells me I am doing things the wrong way.

I have a small sandbox set up for testing where I have a few client and server components that I use to test if data is being passed around.

page.tsx
import React from 'react';
import ContextCC from "./contenxt_cc";
import ContextSC from "./context_sc";
import ContextChildCC from './context_child_cc';

const Page: React.FC = () => {
  const data = "We shared data!";
  return (
    <div>
      <p> This is a server component - {data} </p>
      <ContextCC data={data}>
        <ContextSC data={data} />
        <ContextChildCC />
      </ContextCC>
    </div>
  );

};

export default Page;


context_cc.tsx
'use client'
import React, { useContext } from 'react';
import AppContext from './app_context';

export default function ContextCC({ children, data }: { children: React.ReactNode, data: string }) { 
  const sharedData = {
    sharedMessage: data,
  }
  return (
    <AppContext.Provider value={sharedData}>
      <div>
        <p> This is a client component - {sharedData.sharedMessage} </p>
        { children }
      </div>
    </AppContext.Provider>
  );
}


context_sc.tsx
export default function ContextSC({data}: { data: string }) {
  return (
    <div>
      <p> This is a server component - { data }</p>
    </div>
  );
}


contect_child_cc
"use client";
import { useContext } from "react";
import AppContext from "./app_context";
import { printAuthToken } from "./server_action";
import { useState } from "react";

export default function ContextChildCC() {
  const [serverRes, setServerRes] = useState("");
  const context = useContext(AppContext);
  const action = async () => {
    const result = await printAuthToken(context.sharedMessage)
    setServerRes(result.msg);
  }
  return(
    <div>
      <p> This is a child client component - {context.sharedMessage} </p>
      <p> {serverRes} </p>
      <button onClick={action}> Send </button>
    </div>
  )
}


This gives me the output that I would expect. It just feels like there should be a smarter way to pass data around. Like If I have a server component that would need to pass along a auth-token I don't like having to ensure that I am passing the prop. I feel like I could easily forget to do it and brake things.

Any ideas or tips would be super helpful thanks ❤️

Update:

I also made a server action and I feel like this flow is better. Is the way to go just use server actions over server components? Maybe what I am trying to understand better is when to use server components over server actions over client components? This is all kinda confusing to me >,<

"use server"

export async function printAuthToken(authToken: string) {
  console.log(authToken);
  return { msg: "This is a response from a server action - " + authToken }
}
image.png
Was this page helpful?