Search code examples
reactjssupabaseastrojssupabase-js

React component within Astro is not working, can't access env variables


I have recently begun using Astro and am a little confused about how to properly use react components within the framework. I'm sure my error is likely a silly one, I am not a professional developer and this is all quite new to me.

My navbar is not working. When I click on the mobile menu button, nothing happens, and on page load, I am receiving the following error: [Error] [astro-island] Error hydrating /src/components/Navbar.tsx Error: Missing Supabase URL or anon key Module Code — supabase.ts:7 (anonymous function) (localhost:1171:3738)

Here is my Navbar component:

import { useState, useEffect } from "react";
import { Menu, X, User, LogOut } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { supabase } from "@/lib/supabase";

export const Navbar = () => {
  const [open, setOpen] = useState(false);
  const [loggedIn, setLoggedIn] = useState(false);

  const checkUser = async () => {
    const session = await supabase.auth.getSession();
    if (session) {
      setLoggedIn(true);
    } else {
      setLoggedIn(false);
    }
  };

  checkUser();

  return (
    <nav className="bg-white border-b sticky top-0">
      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
        <div className="relative flex items-center justify-between h-16">
          <div className="flex-1 flex items-center justify-start sm:items-stretch sm:justify-start">
            <div className="flex-shrink-0">
              <a href="/">
                <img className="h-8 w-auto" src="/favicon.svg" alt="Workflow" />
              </a>
            </div>
            <div className="hidden sm:block sm:ml-6">
              <div className="flex space-x-2">
                <a
                  href="#"
                  className="text-gray-900 hover:text-gray-500 hover:text-white px-3 py-2 text-sm"
                >
                  Stories
                </a>
                <a
                  href="#"
                  className="text-gray-900 hover:text-gray-500 hover:text-white px-3 py-2 text-sm"
                >
                  Writers
                </a>
                <a
                  href="#"
                  className="text-gray-900 hover:text-gray-500 hover:text-white px-3 py-2 text-sm"
                >
                  Blog
                </a>
                <a
                  href="#"
                  className="text-gray-900 hover:text-gray-500 hover:text-white px-3 py-2 text-sm"
                >
                  About
                </a>
              </div>
            </div>
          </div>
          <div className="flex items-center space-x-2">
            {!loggedIn && (
              <>
                <Button className="hidden sm:inline-block" variant="outline">
                  Login
                </Button>
                <Button className="hidden sm:inline-block">Sign Up</Button>
              </>
            )}
            {loggedIn && (
              <DropdownMenu>
                <DropdownMenuTrigger>
                  <Avatar className="h-8 w-8">
                    <AvatarImage src="https://github.com/shadcn.png" />
                    <AvatarFallback>CN</AvatarFallback>
                  </Avatar>
                </DropdownMenuTrigger>
                <DropdownMenuContent>
                  <a href="/profile">
                    <DropdownMenuItem>
                      <User />
                      My Profile
                    </DropdownMenuItem>
                  </a>
                  <DropdownMenuItem>
                    <LogOut />
                    Log out
                  </DropdownMenuItem>
                </DropdownMenuContent>
              </DropdownMenu>
            )}
            <div className="sm:hidden">
              <button
                type="button"
                className="inline-flex items-center justify-center text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"
                aria-controls="mobile-menu"
                aria-expanded={open ? "true" : "false"}
                onClick={() => setOpen(!open)}
              >
                <span className="sr-only">Open main menu</span>
                {open ? (
                  <X className="block h-6 w-6" aria-hidden="true" />
                ) : (
                  <Menu className="block h-6 w-6" aria-hidden="true" />
                )}
              </button>
            </div>
          </div>
        </div>
      </div>

      <div
        className={`sm:hidden ${open ? "block" : "hidden"}`}
        id="mobile-menu"
      >
        <div className="px-2 pt-2 pb-3 space-y-1">
          <a
            href="#"
            className="text-gray-900 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium"
          >
            Dashboard
          </a>
          <a
            href="#"
            className="text-gray-900 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium"
          >
            Team
          </a>
          <a
            href="#"
            className="text-gray-900 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium"
          >
            Projects
          </a>
          <a
            href="#"
            className="text-gray-900 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium"
          >
            Calendar
          </a>
          <div className="mt-4 space-y-1">
            <Button className="w-full" variant="outline">
              Login
            </Button>
            <Button className="w-full">Sign Up</Button>
          </div>
        </div>
      </div>
    </nav>
  );
};

My .env file is properly set up, and my .lib/supabase.ts file is as following:

import { createClient } from "@supabase/supabase-js";

const supabaseUrl = import.meta.env.SUPABASE_URL;
const supabaseAnonKey = import.meta.env.SUPABASE_ANON_KEY;

if (!supabaseUrl || !supabaseAnonKey) {
  throw new Error("Missing Supabase URL or anon key");
}

export const supabase = createClient(supabaseUrl, supabaseAnonKey);

I'd really appreciate any pointers here.


Solution

  • From Astro: Using environment variables:

    Note that while all environment variables are available in server-side code, only environment variables prefixed with PUBLIC_ are available in client-side code for security purposes.

    So it should be:

    const supabaseUrl = import.meta.env.PUBLIC_SUPABASE_URL;
    const supabaseAnonKey = import.meta.env.PUBLIC_SUPABASE_ANON_KEY;
    

    And yes, those two are safe to expose on the client, assuming:

    you have Row Level Security enabled.