Thought leadership from the most innovative tech companies, all in one place.

Embedding Google Forms in React Apps

A comprehensive guide starting from embedding Google Forms as an iframe to any customized UI with styling and branding

Have you ever built a website? Have you ever thought about contacting your users to get feedback?

If your answer is yes to both of questions, there is a powerful tool to collect feedback from users, which is called Google Forms.

Google Forms is a survey administration software. It is part of the free, web-based Google Docs Editors suite, including Google Docs, Google Sheets, Google Slides, Google Drawings, Google Sites, and Google Keep. Google Forms can manage event registrations, create a quick opinion poll, and perform much more tasks.

Let’s take a look at how to use Google Forms in React apps, starting from embedding it as an iframe to any customized UI with styling and branding.

Create Google Forms

Go to [https://docs.google.com/forms](https://docs.google.com/forms), it shows a list of form templates and recently used forms:

Click the colorful + sign to start a new form, which has three tabs, Questions, Responses, and Settings.

Here is the first tab, Questions.

It builds a form with title, description, and various form components. Clicking on the select, showing Multiple choice, we see various form components, including Short answer, Paragraph, Multiple choice, Checkboxes, Dropdown, File upload, Linear scale, Multiple choice grid, Checkbox grid, Date, and Time.

Using this UI, we have built a survey for React usage:

Click on the button, Send, and it shows the choices to send the form via Email, Link, or Embed HTML. If the choice is Email, an email list needs to be provided:

If the choice is Link, the link can be copied, long or short version:

If the choice is Embed HTML, an iframe tag can be copied:

Here is the copied iframe tag with default width and height set to 640px and 685px.

<iframe
  src="https://docs.google.com/forms/d/e/1FAIpQLSeZr6hSDJXdfbnNQ2omeMYLd0SpLwABU3BYUK0mpEzuILdcBQ/viewform?embedde``d=true"
  width="640"
  height="685"
  frameborder="0"
  marginheight="0"
  marginwidth="0"
>
  Loading…
</iframe>

Go back to the choice of Email, and click the button, Send.

We receive the email immediately. On the email content, there is a button, FILL OUT IN GOOGLE FORMS.

It opens a new tab/window. Fill in fields and click the button, Submit.

Go back to the form builder, and choose the second tab, Responses. It shows one response received and one response pending, along with statistical data:

Click on the green button at the top of form, Link to Sheet, and it allows us to save responses to a spreadsheet.

Google Forms is a web application. It works out of the box, and we are going to use it in a React app.

Set Up Ant Design System in Create React App

We use Create React App as a base to embed Google Forms. The following command creates a React project:

% yarn create react-app react-google-form
% cd react-google-form

Ant Design System is an open source code for enterprise-level UI design languages and React UI library. We install antd to take advantage of its rich component library to build the app.

% yarn add antd

It becomes part of dependencies in package.json:

"dependencies": {
  "antd": "^5.3.2"
}

The working environment is ready.

Build Google Forms App

Ant Design System has many UI components, such as button, icon, typography, modal, select, table, and tree components. We are going to embed Google Forms using FloatButton and Drawer, along with an icon, LikeOutlined.

The FloatButton Component

FloatButton is used for global functionality on the site. It can be seen wherever you browse. We place the button at the lower right corner.

Here is the code:

<FloatButton
  icon={<LikeOutlined />}
  description="Survey"
  onClick={() => setOpen(true)}
  style={{ width: 80, height: 80, bottom: 24, right: 24 }}
/>

Optionally, FloatButton can have icon and description. Upon clicking, we make it to open the drawer (setOpen(true)). The button size has been configured to be 80px x 80px, and the distance to bottom and right are both 24px.

The Drawer Component

Drawer is a panel which typically overlaid on top of a page and slides in from the side. Here is the drawer after clicking the survey button:

Here is the code:

<Drawer
  title="Survey"
  placement="right"
  onClose={() => setOpen(false)}
  open={open}
  size="large"
>
  {children}
</Drawer>

Drawer has title, and placement, which can be 'left' | 'right' | 'top' | 'bottom'. The drawer is open if the open state is true. When clicking the X button or outside of the drawer, we close the open drawer. Drawer supports two sizes, "default" or "large". For our survey, it is set to "large".

Copy the iframe tag, and paste it as the child of Drawer, adjusting some props to fill up the drawer area.

<Drawer
  title="Survey"
  placement="right"
  onClose={() => setOpen(false)}
  open={open}
  size="large"
>
  <iframe
    title="Survey"
    src="https://docs.google.com/forms/d/e/1FAIpQLSeZr6hSDJXdfbnNQ2omeMYLd0SpLwABU3BYUK0mpEzuILdcBQ/viewform?embedded=true"
    frameborder="0"
    marginheight="0"
    marginwidth="0"
    style={{ width: "100%", height: "calc(100% - 3px)" }}
  >
    Loading…
  </iframe>
</Drawer>

Here is the Google Forms displayed on the drawer:

The Complete Code

Here is the complete code of the modified src/App.js:

import { useState } from "react";
import logo from "./logo.svg";
import "./App.css";
import { Drawer, FloatButton } from "antd";
import { LikeOutlined } from "@ant-design/icons";

function App() {
  const [open, setOpen] = useState(false);
  return (
    <>
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header>
      </div>
      <FloatButton
        icon={<LikeOutlined />}
        description="Survey"
        onClick={() => setOpen(true)}
        style={{ width: 80, height: 80, bottom: 24, right: 24 }}
      />
      <Drawer
        title="Survey"
        placement="right"
        onClose={() => setOpen(false)}
        open={open}
        size="large"
      >
        <iframe
          title="Survey"
          src="https://docs.google.com/forms/d/e/1FAIpQLSeZr6hSDJXdfbnNQ2omeMYLd0SpLwABU3BYUK0mpEzuILdcBQ/viewform?embedded=true"
          frameborder="0"
          marginheight="0"
          marginwidth="0"
          style={{ width: "100%", height: "calc(100% - 3px)" }}
        >
          Loading…
        </iframe>
      </Drawer>
    </>
  );
}

export default App;

Build Customized Google Forms

Embedding Google Forms as an iframe works. However, we may want to have a customized UI with styling and branding. Google Forms enables us to do it by two steps:

  • Get pre-filled link.
  • Build a customized UI.

Get Pre-Filled Link

Go back to the form builder page. There is a list of available commands, upon clicking the more menu (also called the kebab menu, the three-dot menu, and the vertical three-dot menu).

Select Get pre-filled link:

It shows a survey UI to be pre-filled. It says that Email cannot be pre-filled, but do fill in answers for other questions.

Click the button Get link, and the following is the saved link from the clip board:

https://docs.google.com/forms/d/e/1FAIpQLSeZr6hSDJXdfbnNQ2omeMYLd0SpLwABU3BYUK0mpEzuILdcBQ/viewform?usp=pp_url&entry.2057655542=React+18&entry.1743450499=8+months

The link shows internal IDs of the fields:

  • entry.2057655542: It is the ID for React version’s choice.
  • entry.1743450499: It is the ID for the duration of use.

There is another field missing, emailAddress, as it cannot be pre-filled. We append an email address to the saved link:

https://docs.google.com/forms/d/e/1FAIpQLSeZr6hSDJXdfbnNQ2omeMYLd0SpLwABU3BYUK0mpEzuILdcBQ/viewform?usp=pp_url&entry.2057655542=React+18&entry.1743450499=8+months&emailAddress=test@yahoo.com

Copy and paste the link to the browser URL, and we can confirm that all fields are populated correctly.

After replacing viewform with formResponse, the link can be used to send responses to Google Forms.

Build a Customized UI

We would like to create a custom UI using Ant Design System.

The Form Component

Form allows a user to enter data that is sent to a server for processing. Ant Design System provides Button, Form, Input, Radio, and many components that are wrapped by Form.Item.

The Email Input

The custom UI requires an email, which is a string input. The Form.Item is named email.

<Form.Item
  name="email"
  label="Email"
  rules={[{ required: true, message: "Email address is required" }]}
>
  <Input />
</Form.Item>

The React Version Radio Group

The custom UI requires to select the React version, which is a group of Radio buttons. The Form.Item is named version.

<Form.Item
  name="version"
  label="1. Which version of React are you using?"
  rules={[{ required: true, message: "React version is required" }]}
>
  <Radio.Group>
    <Space direction="vertical">
      <Radio value="React 16">React 16</Radio>
      <Radio value="React 17">React 17</Radio>
      <Radio value="React 18">React 18</Radio>
    </Space>
  </Radio.Group>
</Form.Item>

The Duration of Use Input

The custom UI can optionally have the duration of use, which is a string input. The Form.Item is named duration.

<Form.Item
  name="duration"
  label="2. How long have you been using React version from Question 1?"
>
  <Input />
</Form.Item>

The Submit Button

The form has a submit button. Since no data is used, it works with or without being wrapped by Form.Item.

<Button type="primary" htmlType="submit">
  Submit
</Button>

When the button is clicked, the form is validated:

After all required fields are validated, Submit works.

The form’s onFinish method is invoked with an object of the populated data. The above input generates the following object:

{
  "email": "test@google.com",
  "version": "React 18",
  "duration": "6 months"
}

This object is used to populate formResponse's query parameters, with internal IDs.

const onFinish = useCallback(
  async ({ email, version, duration }) => {
    try {
      await fetch(
        "https://docs.google.com/forms/d/e/1FAIpQLSeZr6hSDJXdfbnNQ2omeMYLd0SpLwABU3BYUK0mpEzuILdcBQ/formResponse?" +
          new URLSearchParams({
            "entry.2057655542": version,
            "entry.1743450499": duration ?? "",
            emailAddress: email,
          }),
        {
          mode: "no-cors",
        }
      );
    } catch (e) {
      console.log(e.message);
    }
  },
  [api]
);

We have to set mode to 'no-cors', as this app located localhost:3000 fetching to https://docs.google.com causes the violation of Cross-Origin Resource Sharing (CORS). 'no-cors' works, but we should put this kind of service into the backend, such as a node.js server.

The Complete Demo

For a complete demo, we also use Ant Design System’s notification to send out a success (api.success) message or an error (api.error) message. For a successful submission, the form data is cleared (form.resetFields()), and the drawer is closed (setOpen(false)). Since Ant Design System divides the design area into 24 sections, the form takes three quarters of the width with span set to 18.

Here is src/CustomGoogleForms.js:

import { useCallback } from "react";
import { Button, Form, Input, Radio, Space, notification } from "antd";

const CustomGoogleForms = ({ setOpen }) => {
  const [form] = Form.useForm();
  const [api, contextHolder] = notification.useNotification();

  const onFinish = useCallback(
    async ({ email, version, duration }) => {
      try {
        await fetch(
          "https://docs.google.com/forms/d/e/1FAIpQLSeZr6hSDJXdfbnNQ2omeMYLd0SpLwABU3BYUK0mpEzuILdcBQ/formResponse?" +
            new URLSearchParams({
              "entry.2057655542": version,
              "entry.1743450499": duration ?? "",
              emailAddress: email,
            }),
          {
            mode: "no-cors",
          }
        );
        api.success({
          message: "Submitted successfully",
        });
        form.resetFields();
        setOpen(false);
      } catch (e) {
        api.error({
          message: e.message,
        });
      }
    },
    [api, form]
  );

  return (
    <>
      {contextHolder}
      <Form
        form={form}
        layout="vertical"
        wrapperCol={{ span: 18 }}
        onFinish={onFinish}
      >
        <Form.Item
          name="email"
          label="Email"
          rules={[{ required: true, message: "Email address is required" }]}
        >
          <Input />
        </Form.Item>
        <Form.Item
          name="version"
          label="1. Which version of React are you using?"
          rules={[{ required: true, message: "React version is required" }]}
        >
          <Radio.Group>
            <Space direction="vertical">
              <Radio value="React 16">React 16</Radio>
              <Radio value="React 17">React 17</Radio>
              <Radio value="React 18">React 18</Radio>
            </Space>
          </Radio.Group>
        </Form.Item>
        <Form.Item
          name="duration"
          label="2. How long have you been using React version from Question 1?"
        >
          <Input />
        </Form.Item>
        <Button type="primary" htmlType="submit">
          Submit
        </Button>
      </Form>
    </>
  );
};

export default CustomGoogleForms;

The created component, CustomGoogleForms, is used in src/App.js:

import { useState } from "react";
import logo from "./logo.svg";
import "./App.css";
import { Drawer, FloatButton } from "antd";
import { LikeOutlined } from "@ant-design/icons";
import CustomGoogleForms from "./CustomGoogleForms";

function App() {
  const [open, setOpen] = useState(false);
  return (
    <>
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
            Edit <code>src/App.js</code> and save to reload.
          </p>
          <a
            className="App-link"
            href="https://reactjs.org"
            target="_blank"
            rel="noopener noreferrer"
          >
            Learn React
          </a>
        </header>
      </div>
      <FloatButton
        icon={<LikeOutlined />}
        description="Survey"
        onClick={() => setOpen(true)}
        style={{ width: 80, height: 80, bottom: 24, right: 24 }}
      />
      <Drawer
        title="Survey"
        placement="right"
        onClose={() => setOpen(false)}
        open={open}
        size="large"
      >
        <CustomGoogleForms setOpen={setOpen} />
      </Drawer>
    </>
  );
}

After a successful submission, the drawer is closed and the success message is displayed.

How to test the failed case?

Simply change mode to 'cors':

mode: "cors";

Upon clicking Submit, the fetch command failed, and the error message is displayed.

The Statistical Data

After more forms are submitted, the statistical data is updated:

Conclusion

Google Forms is a powerful tool to collect feedback from users. We have shown two examples:

  • Embed Google Forms as an iframe in React App.
  • Invoke Google Forms from a customized UI.

Which way do you prefer?

Thanks for reading.




Continue Learning