How to force Gatsby to redirect a specific URL path to an external site? - reactjs

I have a Gatsby site and due to some specific requirements, I need to redirect anyone who attempts to hit a specific URL path, for which there is no page, to an external site. This URL path is not a page within the site, but it's something that a user may be inclined to type due to documentation that is out of my control.
Here's an example: Let's say the site is located at https://www.example.com. A user may visit https://www.example.com/puppies, which does not exist. My file structure does not contain a src/pages/puppies.js file. However, when that URL is entered, I need to redirect the user to another site altogether, such as https://www.stackoverflow.com.

I haven't used Gatsby to that extent to know it has a configuration for this, so someone else may correct me. The way I would handle this is through the hosting provider where your app is.
For example, if you are using Netlify:
Create a _redirects file with the following content:
/* /index.html 200
Or
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
This will cause all https://yourwebsite.com/IDontHaveThisRoute to fallback to /index.html where your .js is loaded.
I provided the Netlify example only to give you the basic idea of how it can be done through the hosting provider of your choice. I would look into configurations I can put into redirects where my domain is deployed.

Thanks to Paul Scanlon he mentioned using onRouteUpdate in Gatsby and it works like a charm
import { navigate } from 'gatsby';
export const onRouteUpdate = ({ location }) => {
if (location.pathname === '/dashboard') {
navigate('/dashboard/reports');
}
};

This question helped point me in the right direction. I was able to get it to work using Gatsby's componentDidMount() to force a redirect as shown below, using a new file called puppies.js to "catch" the path typed by the user:
// puppies.js
import React, { Component } from 'react'
class Puppies extends Component {
componentDidMount() {
window.location.replace("https://www.stackoverflow.com");
}
render() {
return <div />
}
}
export default Puppies

Related

Getting "Key not Found:" while accessing static website on s3

I have a working website which is developed in react and is hosted as a static site on AWS-S3. Our home page route is /home however when we try to access it, we get the following error:
Message: The specified key does not exist.
Key: home/home
RequestId: 0CJ72YSRVM1VR7DT
HostId: EnusXIGM/BWUeBk2Y+jjW2XFYtKjpFB66wf2n9om5L/+yX52JQUviK5ZLPJW6U0moywqGuIBc5M=
so, in the root folder, we have put a index.html in the root folder with the following content.
import React from "react";
import { useNavigate } from "react-router-dom";
const Coverpage: React.FC = () => {
const navigate = useNavigate();
return (
<>
<div>I am cover page!</div>
<button onClick={() => navigate("/home")}>Click me to navigate to homepage!</button>
</>
);
};
export default Coverpage;
and the navigation works.
I really need help to find a way where when the user access the site http://xxxx, the site directly navigate to http://xxxx/home and the site opens as desired.
Appreciate your help. not sure if we can do it at reactjs level or AWS s3
The solution is to configure AWS to rewrite most URLs to your React's index.html.
Create a CloudFront distribution which distributes the S3 bucket.
Ensure the distribution's Default Root Object is set to index.html.
Create a CloudFront function with the code below. This rewrites all requests to index.html except for requests to /static/*.
function handler(event) {
var request = event.request;
var parts = request.uri.split('/');
if(parts[1] != 'static'){
request.uri = '/index.html';
console.log('Rewriting: ' + request.uri);
} else {
console.log('Not Rewriting: ' + request.uri);
}
return request;
}
Go to the CF distribution then open the default behaviour (or create it if needed) and at the bottom, associate the function as a View Request type
Now you can visit the CF Distribution's URL (or your domain if configured) with any path and the React app will show.
The other option is to simply set the S3 bucket's Error Document to index.html, but this is very hacky and not recommended. Page visits will show the React app but will have undesirable 404 error codes.

Next JS - Middlewares - Error: URLs is malformed. Please use only absolute URLs

Good Morning! I've been trying for hours how to make redirect page in middleware. I read the documentation and I'm using NextResponse.redirect('/about') and i get a message that: "Error: URLs is malformed. Please use only absolute URLs -"
If I enter the Absolute URL as: http://localhost:3000/about the browser keeps executing the request several times as shown in the image below.
I try this solution in Middleware Relative URLs, but the error persists.
Does anyone have any solution? Thanks.
Here is my code in _middleware.ts:
import { NextResponse } from 'next/server';
import type { NextFetchEvent, NextRequest } from 'next/server';
export function middleware(request: NextRequest, ev: NextFetchEvent) {
return NextResponse.redirect('/about')
}
The folder and the files localization:
The new version of next does not allow this anymore, indeed:
next/dist/server/web/utils.js (136:0) # Object.validateURL
I have not consulted official docs, while my solution was to pass on the origin:
export function middleware(req: NextRequest): NextResponse | null {
const { pathname, origin } = req.nextUrl
return NextResponse.rewrite(`${origin}/about`)
}

Next JS dynamic routing not working on export production static build

I'm building an app with Next. using nested dynamic routing. In development mode, everything works as expected. But when I deploy the app in production ,When you hit reload (like F5) it gives you 404
use next version : 9.5.2
my directory structure is like this:
pages
-users
-[page]
- [id].tsx
- index.tsx
-index.tsx
Here's how I link to the dynamic page:
<Link href='/users/[page]/[id]' as='/users/edit/${id}'>
<a>Edit</a> </Link>
Here sample code user/[page]/[id].tsx
class User extends React.Component {
constructor(props) {
super(props);
this.state = {
},
};
static getInitialProps({query}) {
return {query}
}
async componentDidMount(){
const { query } = this.props;
if(query?.id){
await this.props.getUserById(query?.id);
}
}
}
Here static build command
`npm run export`
Here static build
my build is a set of HTML, js, css assets I hosted on the web server (Apache, Nginx).
If I clicked in link users from index.tsx page, my URL look '/users/edit/1/' and work fine, but if I refresh the page I get error 404. In the development mode refresh page, everything works fine.
I am stuck on this problem for many days. Do u know how I can fix this?

Cannot use gatsby-plugin-intl for single page site

I'm using Gatsby and I want build a single page site, so without create pages. For achieve this I edited gatsby-node.js with the following code:
exports.onCreatePage = async ({ page, actions }) => {
const { createPage } = actions
if (page.path === "/") {
page.matchPath = "/*"
createPage(page)
}
}
in that case, each request is re-routed to the index.js page, which is the only one.
Then, in the index.js page I have:
const IndexPage = () => {
const intl = useIntl()
const locale = intl.locale
return (
<BGTState>
<BlogState>
<Layout>
<Router>
<Home path={`${locale}/`} />
<Section path={`${locale}/:sectionSlug`} />
<Collection path={`${locale}/:sectionSlug/:collectionSlug`} />
<Season
path={`${locale}/:categorySlug/:collectionSlug/:seasonSlug`}
/>
<Product
path={`${locale}/:categorySlug/:collectionSlug/:seasonSlug/:prodSlug`}
/>
<Blog path={`${locale}/blog`} />
<Article path={`${locale}/blog/:articleSlug`} />
<NotFound default />
</Router>
</Layout>
</BlogState>
</BGTState>
)
}
as you can see, I have different routers that load a specific component based on the url.
I have prefixed each path with the current locale to match the correct path.
This mechanism is working fine for the home page only, but for the other links doesn't work. Infact, if I visit something like:
http://localhost:3001/en/category-home/prod-foo
which must load the Collection component, the site simply redirect to:
http://localhost:3001/en
and display the Home component again.
What I did wrong?
UPDATE
Page Structure:
As you can see I have just the index.js which handle all requests as I configured in the gatby-node.js.
If I remove the localization plugin, at least using this configuration:
{
resolve: `gatsby-plugin-intl`,
options: {
// Directory with the strings JSON
path: `${__dirname}/src/languages`,
// Supported languages
languages: ["it", "en", "ci", "fr"],
// Default site language
defaultLanguage: `it`,
// Redirects to `it` in the route `/`
//redirect: true,
// Redirect SEO component
redirectComponent: require.resolve(
`${__dirname}/src/components/redirect.js`
),
},
},
and I don't prefix the url with intl.locale, everything is working fine. But adding redirect: true in the plugin configuration, and prefixing the link with the locale, the site redirect me to the home component.
If you are creating a SPA (Single Page Application, notice the single) you won't have any created pages but index. You are trying yo access to a /category page that's not created because of:
if (page.path === "/") {
page.matchPath = "/*"
createPage(page)
}
That's why your routes don't work (or in other words, only the home page works).
Adapt the previous condition to your needs to allow creating more pages based on your requirements.
I'm using Gatsby and I want build a single page site, so without
create pages. For achieve this I edited gatsby-node.js with the
following code:
It's a non-sense trying to build a SPA application with Gatsby (without creating pages) but then complaining because there's not collection page created.
Make sure that you understand what you are doing, it seems clearly that you need to create dynamically pages for each collection, season, and product so your approach to create SPA won't work for your use-case.
It's possible to keep just index.js without overcomplicating thing? I
just want to understand why my code isn't working 'cause I've passed
the correct url... Removing the localization Gatsby works, so I
suspect there is a localization problem
The only way that http://localhost:3001/category-home/prod-foo (removing the localization) could be resolved is by creating a folder structure such /pages/category-home/prod-foo.js (since Gatsby extrapolates the folder structure as URLs), so, if you want to use localization using your approach, add a structure such en/pages/category-home/prod-foo.js and es/pages/category-home/prod-foo.js (or the whatever locale), and so on. In my opinion, this is overcomplexitying stuff since, for every category, you'll need to create 2 (even more depending on the locales) files.
Gatsby allows you to create dynamic pages and interpolate the locale automatically using built-in plugins on the process, creating each file for the specifically defined locales.

How to check if asset was added from public/ dir in React?

Is it possible to check if a file exists within the /public/ directory?
I have a set of images that correspond to some objects. When available, I would like to display them using <img> tag. However not all of the objects have a corresponding image, in which case I would like to perform a REST request to our server.
I could create a list of files as part of build process, but I would like to avoid that if possible.
I am using create-react-app if it matters (if I understand correctly fs doesn't work in client-side React apps).
EDIT: I guess I should have been more exact in my question - I know client-side JS can't access this information (except through HTTP requests), I was just hoping something saves information (during build) about the files available in a way that is accessible to client-side Javascript... Maybe Webpack or some extension can do this?
You can do this with your axios by setting relative path to the corresponding images folder. I have done this for getting a json file. You can try the same method for an image file, you may refer these examples
Note: if you have already set an axios instance with baseurl as a server in different domain, you will have to use the full path of the static file server where you deploy the web application.
axios.get('http://localhost:3000/assets/samplepic.png').then((response) => {
console.log(response)
}).catch((error) => {
console.log(error)
})
If the image is found the response will be 200 and if not, it will be 404.
Edit: Also, if the image file is present in assets folder inside src, you can do a require, get the path and do the above call with that path.
var SampleImagePath = require('./assets/samplepic.png');
axios.get(SampleImagePath).then(...)
First of all you should remember about client-server architecture of any web app. If you are using create-react-app you are serving your app via webpack-dev-server. So you should think about how you will host your files for production. Most common ways are:
apache2 / nginx
nodejs
but there is a lot of other ways depending on your stack.
With webpack-dev-server and in case you will use apache2 / nginx and if they would be configured to allow direct file access - it is possible to make direct requests to files. For example your files in public path so
class MyImage extends React.Component {
constructor (props) {
super(props);
this.state = {
isExist: null
}
}
componentDidMount() {
fetch(MY_HOST + '/public/' + this.props.MY_IMAGE_NAME)
.then(
() => {
// request status is 200
this.setState({ isExist: true })
},
() => {
// request is failed
this.setState({ isExist: false })
}
);
}
render() {
if (this.state.isExist === true) {
return <img src={ MY_HOST + "/public/" + this.props.MY_IMAGE_NAME }/>
}
return <img src="/public/no-image.jpg"/>
}
}

Resources