Search code examples
reactjsmaterial-uiastrojs

Passing react elements as props in Astro when using MUI


I have been experimenting with to using MUI React components in my Astro project. So far the thing I have tried works great.

I currently try to implement login form and have run into an issue with the element FormControlLabel. FormControlLabel has a mandatory prop called control and according to the docs normally you would assign a value like this in React:

  <FormControlLabel control={<Checkbox defaultChecked />} label="Label" />

Initially I tried mapping that to Astro with:

<FormControlLabel client:only="react" control=<Checkbox defaultChecked /> label="Label" />


           

However there is an issue with closing the tag. Seems like the /> of the checkbox is registered as the end of the FormControlLabel element. Which makes sense.

I tried everything I could imagine including wrapping it all in parathesis, however I have not been able to find a solution. However I do not have much knowledge with Astro.

Can any of you think of a solution that could work?

For reference here is my current working example of using MUI components on an astro page. Still needs some work before its finished, but its a great start of an experiment.

---
import {CredentialsForm} from "../components/CredentialsForm";
import TextField from '@mui/material/TextField';
import FormControlLabel from '@mui/material/FormControlLabel';
import Button from '@mui/material/Button';
import Checkbox from '@mui/material/Checkbox';
import ThemeProvider from '@mui/material/ThemeProvider';
import Checkbox from '@mui/material/Checkbox';
import {ICredentials} from "../_types"; import {logIn} from "../auth/authRequests"; import * as console from "console"; import {
    Checkbox, createTheme
} from "@mui/material";
import Layout from '../layouts/Layout.astro';
import * as React from 'react';
import Avatar from '@mui/material/Avatar';
import CssBaseline from '@mui/material/CssBaseline';
import Checkbox from '@mui/material/Checkbox';
import Link from '@mui/material/Link';
import Grid from '@mui/material/Grid';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import Container from '@mui/material/Container';
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
import * as React from 'react';


---

<Layout title="Log in">
    <main>
        <div class="container box">
            <Container client:only="react" component="main" maxWidth="xs">
                <CssBaseline client:only="react"/>
                <Box client:only="react"
                     sx={{
                         marginTop: 8,
                         display: 'flex',
                         flexDirection: 'column',
                         alignItems: 'center',
                     }}
                >
                    <Avatar client:only="react" sx={{m: 1, bgcolor: 'secondary.main'}}>
                        <LockOutlinedIcon client:only="react"/>
                    </Avatar>
                    <Typography client:only="react" component="h1" variant="h5">
                        Sign in
                    </Typography>
                    <Box client:only="react" component="form" onSubmit={() => {
                    }} noValidate sx={{mt: 1}}>
                        <TextField client:only="react"
                                   margin="normal"
                                   required
                                   fullWidth
                                   id="email"
                                   label="Email Address"
                                   name="email"
                                   autoComplete="email"
                                   autoFocus
                        />
                        <TextField client:only="react"
                                   margin="normal"
                                   required
                                   fullWidth
                                   name="password"
                                   label="Password"
                                   type="password"
                                   id="password"
                                   autoComplete="current-password"
                        />
                        <Button client:only="react"
                                type="submit"
                                fullWidth
                                variant="contained"
                                sx={{mt: 3, mb: 2}}
                        >
                            Sign In
                        </Button>
                        <Grid client:only="react" container>
                            <Grid client:only="react" item xs>
                                <a href="#">
                                    Forgot password?
                                </a>
                            </Grid>
                            <Grid client:only="react" item>
                                <a href="#">
                                    {"Don't have an account? Sign Up"}
                                </a>
                            </Grid>
                        </Grid>
                    </Box>
                </Box>
            </Container>

        </div>


    </main>
</Layout>

Solution

  • There are a few issues:

    • You can't use JSX inside props in .astro files
    • .astro files are built server side so you can't pass client side functions/components as a prop
    • It is bad practice to nest client directives (client:only="react")

    To avoid these issues you could wrap your React components in another React component:

    ---
    import Layout from '../layouts/Layout.astro';
    import Wrapper from '../components/Wrapper.jsx';
    ---
    
    <Layout title="Log in">
        <main>
            <div class="container box">
                <Wrapper client:only="react"/>
            </div>
        </main>
    </Layout>