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.
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<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6 <meta http-equiv="X-UA-Compatible" content="ie=edge" />
7 <title>Static Template</title>
8 <link
9 rel="stylesheet"
10 href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
11 integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO"
12 crossorigin="anonymous"
13 />
14 </head>
15 <body>
16 <!-- -->
17
18 <script
19 src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
20 integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
21 crossorigin="anonymous"
22 ></script>
23 <script
24 src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"
25 integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49"
26 crossorigin="anonymous"
27 ></script>
28 <script
29 src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"
30 integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy"
31 crossorigin="anonymous"
32 ></script>
33 </body>
34</html>
Create a button that will trigger the popover. Give it an id
of value popover
.
1<button type="button" class="btn btn-lg btn-primary" id="popover">
2 Click to toggle popover
3</button>
Before the body ends, write a <script>
block and initialize the popover by selecting the id of the button.
1<script>
2 $(document).ready(function() {
3 $("button#popover").popover();
4 });
5</script>
As desired, pass a few options into the popover()
to display HTML content in it.
1// ..
2
3$("button#popover").popover({
4 html: true,
5 content: `<div>Hello</div>`
6});
7// ..
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.
First, add the React CDN links to the HTML file.
1<script
2 crossorigin
3 src="https://unpkg.com/react@16/umd/react.development.js"
4></script>
5<script
6 crossorigin
7 src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"
8></script>
Since there are no modulers or transpilers, you will have to use the React.createElement()
function to create a component instead of writing JSX.
1const Hello = props => React.createElement("div", null, `Hello ${props.to}!`);
Now, where do you render this component? Oh, that's simple you can do the following, right?
1$(document).ready(function() {
2 $("button#popover").popover({
3 html: true,
4 content: `<div id='app'></div>`
5 });
6 const Hello = props => React.createElement("div", null, `Hello ${props.to}!`);
7
8 ReactDOM.render(
9 React.createElement(Hello, { to: "World" }, null),
10 document.getElementById("app")
11 );
12});
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$(document).ready(function() {
2 const a = document.createElement("div");
3
4 const Hello = props => React.createElement("div", null, `Hello ${props.to}!`);
5
6 ReactDOM.render(React.createElement(Hello, { to: "World" }, null), a);
7
8 $("button#popover").popover({
9 html: true,
10 content: a.innerHTML
11 });
12});
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.
Add reactstrap
, bootstrap
, and jquery
in your dependencies.
1npm i reactstrap bootstrap jquery
In the main file, create a basic popover .
1// ...
2import "bootstrap/dist/css/bootstrap.min.css";
3import { Button, Popover, PopoverHeader, PopoverBody } from "reactstrap";
4
5const App = props => {
6 const [popoverOpen, setPopoverOpen] = useState(false);
7
8 const toggle = () => setPopoverOpen(!popoverOpen);
9
10 return (
11 <div
12 className="d-flex align-items-center justify-content-center"
13 style={{ height: "100vh" }}
14 >
15 <Button id="Popover1" type="button" color="primary">
16 Launch Popover
17 </Button>
18 <Popover
19 placement="bottom"
20 isOpen={popoverOpen}
21 target="Popover1"
22 toggle={toggle}
23 >
24 <PopoverHeader>Popover Title</PopoverHeader>
25 <PopoverBody>This the Popover body</PopoverBody>
26 </Popover>
27 </div>
28 );
29};
30// ...
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// ...
2const Counter = () => {
3 const [counter, setCounter] = useState(0);
4
5 return (
6 <div>
7 <Button onClick={() => setCounter(counter + 1)}>Click</Button>
8
9 <span className="ml-3">{counter}</span>
10 </div>
11 );
12};
13// ...
Inside the <PopoverBody />
component, call the counter.
1<PopoverBody>This the Popover body</PopoverBody>
Now, when you click on the popover button, it displays the Popover
component with a counter embedded inside of it
.
Putting it all together, you'll have the components as follows.
1import React, { useState } from "react";
2import "./styles.css";
3import "bootstrap/dist/css/bootstrap.min.css";
4import { Button, Popover, PopoverHeader, PopoverBody } from "reactstrap";
5
6const Counter = () => {
7 const [counter, setCounter] = useState(0);
8
9 return (
10 <div>
11 <Button onClick={() => setCounter(counter + 1)}>Click</Button>
12
13 <span className="ml-3">{counter}</span>
14 </div>
15 );
16};
17
18const App = props => {
19 const [popoverOpen, setPopoverOpen] = useState(false);
20
21 const toggle = () => setPopoverOpen(!popoverOpen);
22
23 return (
24 <div
25 className="d-flex align-items-center justify-content-center"
26 style={{ height: "100vh" }}
27 >
28 <Button id="Popover1" type="button" color="primary">
29 Launch Popover
30 </Button>
31 <Popover
32 placement="bottom"
33 isOpen={popoverOpen}
34 target="Popover1"
35 toggle={toggle}
36 >
37 <PopoverHeader>Popover Title</PopoverHeader>
38 <PopoverBody>
39 <Counter />
40 </PopoverBody>
41 </Popover>
42 </div>
43 );
44};
45
46export default App;
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.