Author avatar

Gaurav Singhal

How to Render a React Component Inside a Bootstrap Popover

Gaurav Singhal

  • Mar 14, 2020
  • 8 Min read
  • 1,391 Views
  • Mar 14, 2020
  • 8 Min read
  • 1,391 Views
Web Development
Front End Web Development
Client-side Framework
React

Introduction

Sometimes you find yourself wanting more than the libraries you are using have to offer. For example, let's say you want to render a React component inside a bootstrap popover. Sounds easy, right? Well, it turns out not to be that simple. This guide will further explore this scenario and offer two approaches for rendering a component inside a bootstrap popover.

Conventional Static Projects

Assume you have a standard static project with no moduler or transpiler, just very basic HTML files that are bootstrap linked across multiple pages.

You'll have the Bootstrap CSS link in the head, and the jQuery, Popper.js and Bootstrap script just before the body ends. The basic HTML template would look as follows.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Static Template</title>
    <link
      rel="stylesheet"
      href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
      integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO"
      crossorigin="anonymous"
    />
  </head>
  <body>
    <!-- -->

    <script
      src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
      integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
      crossorigin="anonymous"
    ></script>
    <script
      src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"
      integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49"
      crossorigin="anonymous"
    ></script>
    <script
      src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"
      integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy"
      crossorigin="anonymous"
    ></script>
  </body>
</html>
html

Add the Popover Button

Create a button that will trigger the popover. Give it an id of value popover.

1
2
3
<button type="button" class="btn btn-lg btn-primary" id="popover">
  Click to toggle popover
</button>
html

Before the body ends, write a <script> block and initialize the popover by selecting the id of the button.

1
2
3
4
5
<script>
  $(document).ready(function() {
    $("button#popover").popover();
  });
</script>
html

As desired, pass a few options into the popover() to display HTML content in it.

1
2
3
4
5
6
7
// ..

$("button#popover").popover({
  html: true,
  content: `<div>Hello</div>`
});
// ..
js

You can see the popover in action when you click on the button.

Now, instead of directly putting in the content, you will add a React component.

Insert React Component

First, add the React CDN links to the HTML file.

1
2
3
4
5
6
7
8
<script
  crossorigin
  src="https://unpkg.com/[email protected]/umd/react.development.js"
></script>
<script
  crossorigin
  src="https://unpkg.com/[email protected]/umd/react-dom.development.js"
></script>
html

Since there are no modulers or transpilers, you will have to use the React.createElement() function to create a component instead of writing JSX.

1
const Hello = props => React.createElement("div", null, `Hello ${props.to}!`);
js

Now, where do you render this component? Oh, that's simple you can do the following, right?

1
2
3
4
5
6
7
8
9
10
11
12
$(document).ready(function() {
  $("button#popover").popover({
    html: true,
    content: `<div id='app'></div>`
  });
  const Hello = props => React.createElement("div", null, `Hello ${props.to}!`);

  ReactDOM.render(
    React.createElement(Hello, { to: "World" }, null),
    document.getElementById("app")
  );
});
js

Unfortunately, this won't work because the popover is initialized and the HTML content inside the popover is added by jQuery. Hence, ReactDOM.render() method will not be able to find the DOM node.

To get around this problem, create a DOM element and then access its innerHTML property inside the popover content.

1
2
3
4
5
6
7
8
9
10
11
12
$(document).ready(function() {
  const a = document.createElement("div");

  const Hello = props => React.createElement("div", null, `Hello ${props.to}!`);

  ReactDOM.render(React.createElement(Hello, { to: "World" }, null), a);

  $("button#popover").popover({
    html: true,
    content: a.innerHTML
  });
});
js

If you want to display a static component, then this will work for you. But if you need interactivity inside the popover, that's something not possible with Bootstrap currently. Bootstrap does not have proper hooks to facilitate interactive behavior.

But if this was a bundled project with a transpiler, we could use the reactstrap library to add interactivity inside the popover component.

Projects with Reactstrap Library

Add reactstrap, bootstrap, and jquery in your dependencies.

1
npm i reactstrap bootstrap jquery
console

In the main file, create a basic popover .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// ...
import "bootstrap/dist/css/bootstrap.min.css";
import { Button, Popover, PopoverHeader, PopoverBody } from "reactstrap";

const App = props => {
  const [popoverOpen, setPopoverOpen] = useState(false);

  const toggle = () => setPopoverOpen(!popoverOpen);

  return (
    <div
      className="d-flex align-items-center justify-content-center"
      style={{ height: "100vh" }}
    >
      <Button id="Popover1" type="button" color="primary">
        Launch Popover
      </Button>
      <Popover
        placement="bottom"
        isOpen={popoverOpen}
        target="Popover1"
        toggle={toggle}
      >
        <PopoverHeader>Popover Title</PopoverHeader>
        <PopoverBody>This the Popover body</PopoverBody>
      </Popover>
    </div>
  );
};
// ...
jsx

Reactstrap's <Popover /> component allows you to have React components as the children. This means that the children can have state of their own and can provide interactive behavior.

Let's add a <Counter /> component, inside which there will be a <Button /> component that will increment the counter.

1
2
3
4
5
6
7
8
9
10
11
12
13
// ...
const Counter = () => {
  const [counter, setCounter] = useState(0);

  return (
    <div>
      <Button onClick={() => setCounter(counter + 1)}>Click</Button>

      <span className="ml-3">{counter}</span>
    </div>
  );
};
// ...
jsx

Inside the <PopoverBody /> component, call the counter.

1
<PopoverBody>This the Popover body</PopoverBody>
jsx

Now, when you click on the popover button, it displays the Popover component with a counter embedded inside of it .

Final result

Putting it all together, you'll have the components as follows.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import React, { useState } from "react";
import "./styles.css";
import "bootstrap/dist/css/bootstrap.min.css";
import { Button, Popover, PopoverHeader, PopoverBody } from "reactstrap";

const Counter = () => {
  const [counter, setCounter] = useState(0);

  return (
    <div>
      <Button onClick={() => setCounter(counter + 1)}>Click</Button>

      <span className="ml-3">{counter}</span>
    </div>
  );
};

const App = props => {
  const [popoverOpen, setPopoverOpen] = useState(false);

  const toggle = () => setPopoverOpen(!popoverOpen);

  return (
    <div
      className="d-flex align-items-center justify-content-center"
      style={{ height: "100vh" }}
    >
      <Button id="Popover1" type="button" color="primary">
        Launch Popover
      </Button>
      <Popover
        placement="bottom"
        isOpen={popoverOpen}
        target="Popover1"
        toggle={toggle}
      >
        <PopoverHeader>Popover Title</PopoverHeader>
        <PopoverBody>
          <Counter />
        </PopoverBody>
      </Popover>
    </div>
  );
};

export default App;
jsx

Conclusion

This guide showed you how to add a static React component inside a bootstrap popover and how to add interactivity to the popover using a more refined library like reactstrap. These methods will come handy when you are limited by the libraries used in your project. Nevertheless, ensure that you always create a bundled project using templates like create-react-app to avoid fuzzy hacks in the end.

4