Since I'm using React Router to handle my routes in a React app, I'm curious if there is a way to redirect to an external resource.
Say someone hits:
example.com/privacy-policy
I would like it to redirect to:
example.zendesk.com/hc/en-us/articles/123456789-Privacy-Policies
I'm finding exactly zero help in avoiding writing it in plain JavaScript at my index.html loading with something like:
if (window.location.path === "privacy-policy"){
window.location = "example.zendesk.com/hc/en-us/articles/123456789-Privacy-Policies"
}
Here's a one-liner for using React Router to redirect to an external link:
<Route path='/privacy-policy' component={() => {
window.location.href = 'https://example.com/1234';
return null;
}}/>
It uses the React pure component concept to reduce the component's code to a single function that, instead of rendering anything, redirects browser to an external URL.
It works both on React Router 3 and 4.
With Link component of react-router you can do that. In the "to" prop you can specify 3 types of data:
a string: A string representation of the Link location, created by concatenating the location’s pathname, search, and hash properties.
an object: An object that can have any of the following properties:
pathname: A string representing the path to link to.
search: A string representation of query parameters.
hash: A hash to put in the URL, e.g. #a-hash.
state: State to persist to the location.
a function: A function to which current location is passed as an argument and which should return location representation as a string or as an object
For your example (external link):
https://example.zendesk.com/hc/en-us/articles/123456789-Privacy-Policies
You can do the following:
<Link to={{ pathname: "https://example.zendesk.com/hc/en-us/articles/123456789-Privacy-Policies" }} target="_blank" />
You can also pass props you’d like to be on the such as a title, id, className, etc.
There isn’t any need to use the <Link /> component from React Router.
If you want to go to external link use an anchor tag.
<a target="_blank" href="https://meetflo.zendesk.com/hc/en-us/articles/230425728-Privacy-Policies">Policies</a>
It doesn't need to request React Router. This action can be done natively and it is provided by the browser.
Just use window.location.
With React Hooks
const RedirectPage = () => {
React.useEffect(() => {
window.location.replace('https://www.google.com')
}, [])
}
With React Class Component
class RedirectPage extends React.Component {
componentDidMount(){
window.location.replace('https://www.google.com')
}
}
Also, if you want to open it in a new tab:
window.open('https://www.google.com', '_blank');
I actually ended up building my own Component, <Redirect>.
It takes information from the react-router element, so I can keep it in my routes. Such as:
<Route
path="/privacy-policy"
component={ Redirect }
loc="https://meetflo.zendesk.com/hc/en-us/articles/230425728-Privacy-Policies"
/>
Here is my component in case anyone is curious:
import React, { Component } from "react";
export class Redirect extends Component {
constructor( props ){
super();
this.state = { ...props };
}
componentWillMount(){
window.location = this.state.route.loc;
}
render(){
return (<section>Redirecting...</section>);
}
}
export default Redirect;
Note: This is with react-router: 3.0.5, it is not so simple in 4.x
I went through the same issue. I want my portfolio to redirect to social media handles. Earlier I used {Link} from "react-router-dom". That was redirecting to the sub directory as here,
Link can be used for routing web pages within a website. If we want to redirect to an external link then we should use an anchor tag. Like this,
Using some of the information here, I came up with the following component which you can use within your route declarations. It's compatible with React Router v4.
It's using TypeScript, but it should be fairly straightforward to convert to native JavaScript:
interface Props {
exact?: boolean;
link: string;
path: string;
sensitive?: boolean;
strict?: boolean;
}
const ExternalRedirect: React.FC<Props> = (props: Props) => {
const { link, ...routeProps } = props;
return (
<Route
{...routeProps}
render={() => {
window.location.replace(props.link);
return null;
}}
/>
);
};
And use with:
<ExternalRedirect
exact={true}
path={'/privacy-policy'}
link={'https://example.zendesk.com/hc/en-us/articles/123456789-Privacy-Policies'}
/>
The simplest solution is to use a render function and change the window.location.
<Route path="/goToGoogle"
render={() => window.location = "https://www.google.com"} />
If you want a small reusable component, you can just extract it like this:
const ExternalRedirect = ({ to, ...routeProps }) => {
return <Route {...routeProps} render={() => window.location = to} />;
};
and then use it (e.g. in your router switch) like this:
<Switch>
...
<ExternalRedirect exact path="/goToGoogle" to="https://www.google.com" />
</Switch>
I had luck with this:
<Route
path="/example"
component={() => {
global.window && (global.window.location.href = 'https://example.com');
return null;
}}
/>
I solved this on my own (in my web application) by adding an anchor tag and not using anything from React Router, just a plain anchor tag with a link as you can see in the picture screenshot of using anchor tag in a React app without using React Router
Basically, you are not routing your user to another page inside your app, so you must not use the internal router, but use a normal anchor.
Although this is for a non-react-native solution, but you can try.
In React Router v6, component is unavailable. Instead, now it supports element. Make a component redirecting to the external site and add it as shown.
import * as React from 'react';
import { Routes, Route } from "react-router-dom";
function App() {
return(
<Routes>
// Redirect
<Route path="/external-link" element={<External />} />
</Routes>
);
}
function External() {
window.location.href = 'https://google.com';
return null;
}
export default App;
In React Route V6 render props were removed. It should be a redirect component.
RedirectUrl:
const RedirectUrl = ({ url }) => {
useEffect(() => {
window.location.href = url;
}, [url]);
return <h5>Redirecting...</h5>;
};
Route:
<Routes>
<Route path="/redirect" element={<RedirectUrl url="https://google.com" />} />
</Routes>
I think the best solution is to just use a plain old <a> tag. Everything else seems convoluted. React Router is designed for navigation within single page applications, so using it for anything else doesn't make a whole lot of sense. Making an entire component for something that is already built into the <a> tag seems... silly?
To expand on Alan's answer, you can create a <Route/> that redirects all <Link/>'s with "to" attributes containing 'http:' or 'https:' to the correct external resource.
Below is a working example of this which can be placed directly into your <Router>.
<Route path={['/http:', '/https:']} component={props => {
window.location.replace(props.location.pathname.substr(1)) // substr(1) removes the preceding '/'
return null
}}/>
I don't think React Router provides this support. The documentation mentions
A < Redirect > sets up a redirect to another route in your application to maintain old URLs.
You could try using something like React-Redirect instead.
I was facing the same issue and solved it using by http:// or https:// in React.
Like as:
<a target="_blank" href="http://www.example.com/" title="example">See detail</a>
You can use for your dynamic URL:
<Link to={{pathname:`${link}`}}>View</Link>
For V3, although it may work for V4. Going off of Eric's answer, I needed to do a little more, like handle local development where 'http' is not present on the URL. I'm also redirecting to another application on the same server.
Added to the router file:
import RedirectOnServer from './components/RedirectOnServer';
<Route path="/somelocalpath"
component={RedirectOnServer}
target="/someexternaltargetstring like cnn.com"
/>
And the Component:
import React, { Component } from "react";
export class RedirectOnServer extends Component {
constructor(props) {
super();
// If the prefix is http or https, we add nothing
let prefix = window.location.host.startsWith("http") ? "" : "http://";
// Using host here, as I'm redirecting to another location on the same host
this.target = prefix + window.location.host + props.route.target;
}
componentDidMount() {
window.location.replace(this.target);
}
render(){
return (
<div>
<br />
<span>Redirecting to {this.target}</span>
</div>
);
}
}
export default RedirectOnServer;
I am offering an answer relevant to React Router v6 to handle dynamic routing.
I created a generic component called redirect:
export default function Redirect(params) {
window.location.replace('<Destination URL>' + "/." params.destination);
return (
<div />
)
}
I then called it in my router file:
<Route path='/wheretogo' element={<Redirect destination="wheretogo"/>}/>
import React from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
function App() {
return (
<Router>
<Route path="/" exact>
{window.location.replace("http://agrosys.in")}
</Route>
</Router>
);
}
export default App;
Using React with TypeScript, you get an error as the function must return a React element, not void. So I did it this way using the Route render method (and using React router v4):
redirectToHomePage = (): null => {
window.location.reload();
return null;
};
<Route exact path={'/'} render={this.redirectToHomePage} />
Where you could instead also use window.location.assign(), window.location.replace(), etc.
Complementing Víctor Daniel's answer here: Link's pathname will actually take you to an external link only when there's the 'https://' or 'http://' before the link.
You can do the following:
<Link to={{ pathname:
> "https://example.zendesk.com/hc/en-us/articles/123456789-Privacy-Policies"
> }} target="_blank" />
Or if your URL doesn't come with 'https://', I'd do something like:
<Link to={{pathname:`https://${link}`}} target="_blank" />
Otherwise it will prepend the current base path, as Lorenzo Demattécommented.
If you are using server-side rending, you can use StaticRouter. With your context as props and then adding <Redirect path="/somewhere" /> component in your app. The idea is every time React Router matches a redirect component it will add something into the context you passed into the static router to let you know your path matches a redirect component.
Now that you know you hit a redirect you just need to check if that’s the redirect you are looking for. then just redirect through the server. ctx.redirect('https://example/com').
You can now link to an external site using React Link by providing an object to to with the pathname key:
<Link to={ { pathname: '//example.zendesk.com/hc/en-us/articles/123456789-Privacy-Policies' } } >
If you find that you need to use JavaScript to generate the link in a callback, you can use window.location.replace() or window.location.assign().
Over using window.location.replace(), as other good answers suggest, try using window.location.assign().
window.location.replace() will replace the location history without preserving the current page.
window.location.assign() will transition to the URL specified, but will save the previous page in the browser history, allowing proper back-button functionality.
location.replace()
location.assign()
Also, if you are using a window.location = url method as mentioned in other answers, I highly suggest switching to window.location.href = url.
There is a heavy argument about it, where many users seem to adamantly want to revert the newer object type window.location to its original implementation as string merely because they can (and they egregiously attack anyone who says otherwise), but you could theoretically interrupt other library functionality accessing the window.location object.
Check out this conversation. It's terrible.
JavaScript: Setting location.href versus location
I was able to achieve a redirect in react-router-dom using the following
<Route exact path="/" component={() => <Redirect to={{ pathname: '/YourRoute' }} />} />
For my case, I was looking for a way to redirect users whenever they visit the root URL http://myapp.com to somewhere else within the app http://myapp.com/newplace. so the above helped.
I saw that using <React component> and <React render>, may be deprecated in react-router-dom 6.x, so i followed the migration steps of removing the component property, and instead nesting the component as a child of the <Route > node. it worked fine until i got to my routes that use dynamic parameters, and it threw an error.
i changed from...
<Route exact path={`/:category`} component={CategoryPage} />
to...
<Route exact path={`/:category`}>
<CategoryPage />
</Route>
the error is
Exception has occurred: TypeError: Cannot read property 'params' of undefined
at selectParams
it seems pretty straightforward, so is there anything else i need to look at that might be causing this?
UPDATE
i am using useParams in my categoryPage, which works fine while using <React component>
const CategoryPage = () => {
const { category } = useParams()
...
When you make use of children or elements props as specified in react-router v6, the components rendered are not passed the router props directly so you have to make use of useParams, useNavigate, useLocation or useRouteMatch hooks depending on your useCase in the child component if you child component is a functional component
If you child component is a class component, you will make use of withRouter HOC
For your case useParams hook is required to get access to the category param
Please check out this migration guide from v5 to v6 for more details
After a user successfully logs in, I want to push the state of my application to the '/home' route, which exists and can be accessed by changing the url. The problem is I am getting an error that says
"Cannot read property of push state of undefined"
I am trying to use this.props.history.pushState(null, '/home') to accomplish this reroute to the home page, but obviously this.props.history doesn't exist. Can someone help me use the router correctly?
login.jsx
var Login = React.createClass({
getInitialState: function() {
return {
email: null,
password: null
};
},
handleLogin: function(event) {
event.preventDefault();
var props = {
email: this.state.email,
password: this.state.password
};
axios.post('/login', props).then(() => {
console.log(this.props);
this.props.history.pushState(null, '/home'); //Cannot read property of push state of undefined
});
},
app.jsx contains the route definitions
ReactDOM.render(
<Router history={hashHistory}>
<Route path="/" component={Main}>
<Route path="about" component={About} />
<Route path="home" component={Home} />
<IndexRoute component={Signup} />
</Route>
</Router>,
document.getElementById('masterContainer')
);
It seems that you are not passing the history object as a prop to the Login component - that's where the error comes from (but you probably already know that). So you have to either pass it or use the context. The context seems to be more natural and easy way, but the way it can be used depends on the version of react-router.
If you are using the 2.x version, you will have to add this bit to the component class:
contextTypes: {
router: React.PropTypes.object
},
Then you will be able to use this.context.router.push in your component.
See docs for some info about the methods you can use and this example for more insight.
Edit:
Since the 2.4.0 version you can also use the withRouter component wrapper. In this case you will be able to access router through the props (this.props.router) instead of the context. More info about that here.
For version 1.x you can use the History mixin. Also you should have the history prop on the component that you passed to the router - but since this is not the case, maybe you could just pass it from the parent of the Login component.
The context option is also available (this is how the History mixin works), but this time you will have to pass
contextTypes: {
history: React.PropTypes.object
},
Then you will have all the mixin methods in the this.context.history object.
I am not sure how to get clean url with react router.
For the moment I have this:
http://localhost:8889/#/myRoute?_k=qq67x0
I would like to have this:
http://localhost:8889/myRoute
Is there a particular configuration step that I should set to fix this?
This is how I initialize the router:
import { browserHistory, Router, Route, Link, IndexRoute } from 'react-router
And here is my render function:
render((
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute component={MyComponent2} />
<Route path="myComponent1" component={MyComponent1} />
<Route path="myComponent2" component={MyComponent2} />
</Route>
</Router>
), document.getElementById('react-container'))
EDIT:
I have installed the last version of the router and now it works as expected.
Thanks!
Take a look at the documentation under "What is that ?_k=ckuvup junk in the URL?":
When a history transitions around your app with push or replace, it can store "location state" that doesn't show up in the URL on the new location, think of it a little bit like post data in an HTML form.
The DOM API that hash history uses to transition around is simply window.location.hash = newHash, with no place to store location state. But, we want all histories to be able to use location state, so we shim it by creating a unique key for each location and then store that state in session storage. When the visitor clicks "back" and "forward" we now have a mechanism to restore the location state.
The history package docs explain how to opt out, if you want to continue to use the hash history:
If you prefer to use a different query parameter, or to opt-out of this behavior entirely, use the queryKey configuration option.
import createHistory from 'history/lib/createHashHistory'
// Use _key instead of _k.
let history = createHistory({
queryKey: '_key'
})
// Opt-out of persistent state, not recommended.
let history = createHistory({
queryKey: false
})
If you want to use the HTML 5 pushState API, as you mentioned in your question, then you should use browserHistory in your Router configuration instead of hashHistory:
import { Router, browserHistory } from 'react-router'
<Router history={browserHistory}>
...
She the full "Histories" page in the React Router docs for more information.