You can use ReactDOM.createPortal to render a component in a new window as David Gilbertson explains in his post:
Answer from CD.. on Stack Overflowclass MyWindowPortal extends React.PureComponent { constructor(props) { super(props); // STEP 1: create a container <div> this.containerEl = document.createElement('div'); this.externalWindow = null; } render() { // STEP 2: append props.children to the container <div> that isn't mounted anywhere yet return ReactDOM.createPortal(this.props.children, this.containerEl); } componentDidMount() { // STEP 3: open a new browser window and store a reference to it this.externalWindow = window.open('', '', 'width=600,height=400,left=200,top=200'); // STEP 4: append the container <div> (that has props.children appended to it) to the body of the new window this.externalWindow.document.body.appendChild(this.containerEl); } componentWillUnmount() { // STEP 5: This will fire when this.state.showWindowPortal in the parent component becomes false // So we tidy up by closing the window this.externalWindow.close(); } }
You can use ReactDOM.createPortal to render a component in a new window as David Gilbertson explains in his post:
class MyWindowPortal extends React.PureComponent { constructor(props) { super(props); // STEP 1: create a container <div> this.containerEl = document.createElement('div'); this.externalWindow = null; } render() { // STEP 2: append props.children to the container <div> that isn't mounted anywhere yet return ReactDOM.createPortal(this.props.children, this.containerEl); } componentDidMount() { // STEP 3: open a new browser window and store a reference to it this.externalWindow = window.open('', '', 'width=600,height=400,left=200,top=200'); // STEP 4: append the container <div> (that has props.children appended to it) to the body of the new window this.externalWindow.document.body.appendChild(this.containerEl); } componentWillUnmount() { // STEP 5: This will fire when this.state.showWindowPortal in the parent component becomes false // So we tidy up by closing the window this.externalWindow.close(); } }
The upvoted answer works great!
Just leaving a function component version here in case people are searching for that in the future.
const RenderInWindow = (props) => {
const [container, setContainer] = useState(null);
const newWindow = useRef(null);
useEffect(() => {
// Create container element on client-side
setContainer(document.createElement("div"));
}, []);
useEffect(() => {
// When container is ready
if (container) {
// Create window
newWindow.current = window.open(
"",
"",
"width=600,height=400,left=200,top=200"
);
// Append container
newWindow.current.document.body.appendChild(container);
// Save reference to window for cleanup
const curWindow = newWindow.current;
// Return cleanup function
return () => curWindow.close();
}
}, [container]);
return container && createPortal(props.children, container);
};
React - How to open a component in a new window on a button click?
Is it possible to render a component in a new tab in reactjs without setting its route in any page?
Render a react component in a new window
Is there any way to open a new tab in react without refreshing the page.
Videos
» npm install react-new-window
» npm install react-window-open
New Solution based on CreatePortal
import React, { useEffect, useCallback, useMemo, useState } from "react";
import { render, createPortal } from "react-dom";
const App = () => {
const [isOpen, setOpenState] = useState(false);
const open = useCallback(() => setOpenState(true));
const close = useCallback(() => setOpenState(false));
return (
<div>
<h1>Portals in React</h1>
<button onClick={open}>Open</button>
<button onClick={close}>Close</button>
{isOpen && (
<NewWindow close={close}>
Example <button onClick={close}>Close</button>
</NewWindow>
)}
</div>
);
};
const NewWindow = ({ children, close }) => {
const newWindow = useMemo(() =>
window.open(
"about:blank",
"newWin",
`width=400,height=300,left=${window.screen.availWidth / 2 -
200},top=${window.screen.availHeight / 2 - 150}`
)
);
newWindow.onbeforeunload = () => {
close();
};
useEffect(() => () => newWindow.close());
return createPortal(children, newWindow.document.body);
};
render(<App />, document.getElementById("root"));
This worked for me with React 18.
const secondaryWindow = window.open('', '_blank', 'width=600,height=400');
secondaryWindow.document.open();
secondaryWindow.document.write(`
<html>
<head>
<title>My Title</title>
</head>
<body>
<h2>My Header</h2>
<div id="scheduling-grid"></div>
</body>
</html>
`);
const secondaryRoot = secondaryWindow.document.getElementById('scheduling-grid');
const secondaryRootInstance = createRoot(secondaryRoot);
secondaryRootInstance.render(<RelevantComponent prop={prop} />)
I have a use case where I want to open a route in a new tab without refreshing the page.
There is no need to add the curly braces around your urlText variable since it's within the event callback function. If it were to be used in the JSX then curly braces would be required (like <div>{urlText}</div> or <input type="text" value={urlText} />) - but since you're calling a function there is no need to wrap the variable name in curly braces.
The error is caused because by wrapping the urlText variable in curly braces, you are converting urlText to an object: { urlText: "foo" } and thus it appears as [object%20Object] in the URL bar since any object converted to a string will be literally '[object Object]'.
onClick={(event) => {
event.preventDefault();
window.open(urlText, "_blank");
}}
Simply pass the string urlText directly into the function: window.open(urlText, '_blank') without curly brackets.
The curly brackets is syntax to create an object, this function takes in a string argument as its first parameter. So javascript parses your argument into a string [object%20Object], and due to there being no protocol in that it recognises it as a local path.