How to Render a React Component Inside a Bootstrap Popover
Mar 14, 2020 • 8 Minute Read
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.
<!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>
Add the Popover Button
Create a button that will trigger the popover. Give it an id of value popover.
<button type="button" class="btn btn-lg btn-primary" id="popover">
Click to toggle popover
</button>
Before the body ends, write a <script> block and initialize the popover by selecting the id of the button.
<script>
$(document).ready(function() {
$("button#popover").popover();
});
</script>
As desired, pass a few options into the popover() to display HTML content in it.
// ..
$("button#popover").popover({
html: true,
content: `<div>Hello</div>`
});
// ..
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.
<script
crossorigin
src="https://unpkg.com/react@16/umd/react.development.js"
></script>
<script
crossorigin
src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"
></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.
const 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?
$(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")
);
});
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.
$(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
});
});
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.
npm i reactstrap bootstrap jquery
In the main file, create a basic popover .
// ...
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>
);
};
// ...
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.
// ...
const Counter = () => {
const [counter, setCounter] = useState(0);
return (
<div>
<Button onClick={() => setCounter(counter + 1)}>Click</Button>
<span className="ml-3">{counter}</span>
</div>
);
};
// ...
Inside the <PopoverBody /> component, call the counter.
<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.
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;
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.