Search code examples
javascriptastrojs

How to access astro props inside a script tag?


I am a new user testing out Astro. I currently have the following:

index.astro

---
import App from "../components/App.astro"
import KeycloakApp from "../components/KeycloakApp.astro";
const isAuthEnabled = true;   //switch for local dev vs deployment
var isAuthenticated = true;  //placeholder for keycloak component
if (!isAuthenticated) {
    console.log("redirect")
    return Astro.redirect('/Welcome');
}
---
{isAuthEnabled ? <KeycloakApp isAuthenticated={isAuthenticated} /> : <App />}

KeycloakApp.astro

---
import App from "../components/App.astro"
var {isAuthenticated} = Astro.props;
---
<!-- Keycloak wrapper of the regular App -->
<p><button id="flip">click me to flip isAuthenticated!</button></p>
<script>
    function handleClick() {
        console.log(isAuthenticated);
        isAuthenticated = !{isAuthenticated};
        console.log(isAuthenticated);
    }
    document.getElementById("flip").addEventListener("click", handleClick);
</script>
{isAuthenticated && <App />}

The console shows the following error when trying to click on the button:

Uncaught ReferenceError: isAuthenticated is not defined
    at HTMLButtonElement.handleClick (KeycloakApp.astro:9:21)

How can I access the value of isAuthenticated inside my script? The name isAuthenticated cannot be found. enter image description here

I am trying to reference the docs here enter image description here

I am trying to look for a way to access astro props inside a script tag, though if not possible whatsoever, I might just fallback to using a react component with useStates


Solution

  • In Astro the frontmatter is only executed server-side so the props are not available inside your script tag which is executed client side. To be able to use a prop inside the script tag, you need to use define:vars directive. See the Template Directives Reference on Astro documentation.

    ---
    import App from "../components/App.astro"
    var {isAuthenticated} = Astro.props;
    ---
    <!-- Keycloak wrapper of the regular App -->
    <p><button id="flip">click me to flip isAuthenticated!</button></p>
    <script define:vars={{isAuthenticated}}>
        function handleClick() {
            console.log(isAuthenticated);
            isAuthenticated = !{isAuthenticated};
            console.log(isAuthenticated);
        }
        document.getElementById("flip").addEventListener("click", handleClick);
    </script>
    {isAuthenticated && <App />}
    

    The caveat is that when using define:vars, Astro will also apply the is:inline directive so your scripts won’t be bundled and will be inlined directly into the HTML.

    If you don't want that behavior you could try using a data attribute on your button and read the value in your script tag. Something like (I haven't tested):

    ---
    import App from "../components/App.astro"
    var {isAuthenticated = false} = Astro.props;
    ---
    <!-- Keycloak wrapper of the regular App -->
    <p><button id="flip" data-authenticated={`${isAuthenticated}`}>click me to flip isAuthenticated!</button></p>
    <script>
        function handleClick(e) {
            let isAuthenticated = Boolean(e.currentTarget.dataset.authenticated);
            console.log(isAuthenticated);
            isAuthenticated = !{isAuthenticated};
            console.log(isAuthenticated);
            e.currentTarget.dataset.authenticated = String(isAuthenticated);
        }
        document.getElementById("flip").addEventListener("click", handleClick);
    </script>
    {isAuthenticated && <App />}