Using a form within MaterialUI Grid

Viewed 2730

I used MaterialUI's Grid in my React app to split my page into two panes. The right side has two input boxes and a submit button. I'm using spacing={3} to provide spacing among the inputs and the button.

Initial code - https://codesandbox.io/s/cool-morning-mh302


Next, I used react hook form to add a form around the right Grid elements - https://codesandbox.io/s/fervent-chaum-0qgb6

Problem: Adding the <form> removes the spacing provided by the Grid. How can I wrap the input and button within <form> without affecting the original layout (styling/spacing provided by the Grid)?

UPDATE: As one of the answer suggests, I prefer not to hoist the <form> before the Grid.

  1. I feel it is hacky to apply it to non-form elements.
  2. In the future, I might have to support two forms inside the grid.
2 Answers

You should have another grid container inside the form:-

import React from "react";
import { useForm } from "react-hook-form";

import { Grid } from "@material-ui/core";

import "./styles.css";

function onSubmit(data) {
  console.log(data);
}
export default function App() {
  const { register, handleSubmit } = useForm({
    mode: "onBlur"
  });

  return (
    <div className="App">
      <Grid container spacing={2} justify="space-around">
        <Grid item xs={4} container>
          Left pane
        </Grid>

        <Grid item xs={7} container spacing={3}>
          <Grid xs={12} item className="rightPaneHeading">
            Right pane
          </Grid>
          <form onSubmit={handleSubmit(onSubmit)}>
            {/* spacing should be on this grid item container */}
            <Grid item xs={12} container spacing={3}>
              <Grid item xs={12}>
                Input 1<input name="my-input-1" type="text" ref={register} />
              </Grid>
              <Grid item xs={12}>
                Input 2 <input name="my-input-2" type="text" ref={register} />
              </Grid>
              <Grid item xs={12}>
                <button type="submit"> Submit </button>
              </Grid>
            </Grid>
          </form>
        </Grid>
      </Grid>
    </div>
  );
}

Plus you can have two forms in a grid (left or right):-

return (
  <div className="App">
    <Grid container spacing={2} justify="space-around">
      <Grid item xs={4} container>
        Left pane
      </Grid>

      <Grid item xs={7} container>
        <Grid xs={12} item className="rightPaneHeading">
          Right pane
        </Grid>
        <form onSubmit={handleSubmit(onSubmit)}>
          {/* spacing should be on this grid item container */}
          <Grid item xs={12} container spacing={3}>
            <Grid item xs={12}>
              Input 1<input name="my-input-1" type="text" ref={register} />
            </Grid>
            <Grid item xs={12}>
              Input 2 <input name="my-input-2" type="text" ref={register} />
            </Grid>
            <Grid item xs={12}>
              <button type="submit"> Submit </button>
            </Grid>
          </Grid>
        </form>
        <form onSubmit={handleSubmit(onSubmit)}>
          {/* spacing should be on this grid item container */}
          <Grid item xs={12} container spacing={3}>
            <Grid item xs={12}>
              Input 1<input name="my-input-1" type="text" ref={register} />
            </Grid>
            <Grid item xs={12}>
              Input 2 <input name="my-input-2" type="text" ref={register} />
            </Grid>
            <Grid item xs={12}>
              <button type="submit"> Submit </button>
            </Grid>
          </Grid>
        </form>
      </Grid>
    </Grid>
  </div>
  );

Nesting the form element between the Grid container and item elements breaks the spacing because the container applies the spacing style only to its direct children.

One solution is to place the form element outside all the Grid elements. Furthermore, you can remove the requirement that inputs be nested in the form: specify an id on the form element and reference the id in each input's form attribute.

Simplified example:

import React from "react";
import { useForm } from "react-hook-form";

import { Grid } from "@material-ui/core";

function onSubmit(data) {
  console.log(data);
}

export default function App() {
  const { register, handleSubmit } = useForm();

  return (
    <div>
      <form onSubmit={handleSubmit(onSubmit)} id="my-form"></form>

      {/* Complex Grid layout omitted */}

      <input name="my-input" type="text" form="my-form" ref={register} />
      <button type="submit" form="my-form">Submit</button>
    </div>
  );
}
Related