Set Up
Host: S3 bucket server through CloudFront
CI: Github Actions / Terraform
Cloudfront Custom error page 404 set to index.html (replaced with 200)
S3 Error Page set to index.js
Node 16.10.0
React 18.2.0
Edit: React Router Dom 6.12.1 -> BrowserRouter Component
Very strange issue that has me stumped. When My React App is built and deployed via the CI. React Router will change the URL but not navigate to the correct screen. A hard refresh will go to the correct screen.
If I build Manually and Manually upload to S3 through the AWS console, navigation works as expected. Additionally navigation works all the time on localhost.
I can not figure out what the difference is, its stumped me for a few days. If anyone has any ideas it would really make my day.
CI is simply running npm install && npm run build
Here is the terraform resource thats depoying the files:
resource "aws_s3_bucket_object" "app_files" {
for_each = fileset("${path.module}/app/build", "**/*.*")
bucket = aws_s3_bucket.app_bucket.id
key = each.value
source = "${path.module}/app/build/${each.value}"
etag = filemd5("${path.module}/app/build/${each.value}")
content_type = lookup(var.mime_types, split(".", each.value)[length(split(".", each.value)) - 1], "application/octet-stream")
}
I have confirmed that mimetypes are being set properly.
here is my Cloudfront Distribution
resource "aws_cloudfront_distribution" "app_distribution" {
origin {
domain_name = aws_s3_bucket.app_bucket.website_endpoint
origin_id = aws_s3_bucket.app_bucket.id
custom_origin_config {
http_port = 80
https_port = 443
origin_protocol_policy = "http-only"
origin_ssl_protocols = ["TLSv1.2"]
}
}
restrictions {
geo_restriction {
restriction_type = "none"
}
}
enabled = true
is_ipv6_enabled = true
default_root_object = "index.html"
aliases = [var.service_domain]
default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
target_origin_id = aws_s3_bucket.app_bucket.id
forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
viewer_protocol_policy = "redirect-to-https"
min_ttl = 0
default_ttl = 3600
max_ttl = 86400
}
viewer_certificate {
acm_certificate_arn = var.main_certificate_arn
ssl_support_method = "sni-only"
}
custom_error_response {
error_code = 404
response_code = 200
response_page_path = "/index.html"
error_caching_min_ttl = 0
}
}
Here is the JSX for my Router
<Router>
<Routes>
<Route // remove trailing slash
path="*(/+)"
loader={({ params }) => redirect(params['*'] || '/')}
/>
{appStore?.auth?.accessToken ? (
<Route path="" element={<Template />}>
<Route
exact
path="/"
element={<Navigate to={ROUTES.MY_MIRRORS} />}
/>
... Additional App Routes
<Route
path="/forbidden"
element={<Pages.ErrorBoundaryForbidden />}
/>
<Route path="/404" element={<Pages.ErrorBoundary404 />} />
<Route
path="*"
element={<Navigate replace to={ROUTES.MY_MIRRORS} />}
/>
</Route>
) : (
<Route path="" element={<UnauthenticatedTemplate />}>
<Route path="/login" element={<Pages.Login />} />
<Route path="/register" element={<Pages.NewUser />} />
<Route path="/contact" element={<Pages.Contact />} />
<Route path="/privacy-policy" element={<Pages.PrivacyPolicy />} />
<Route path="/" element={<Pages.Home />} />
<Route path="*" element={<Navigate replace to="/login" />} />
</Route>
)}
</Routes>
</Router>
Finally, all navigation is used by the useNavigate hook:
import { useNavigate } from 'react-router-dom';
...
const navigate = useNavigate();
...
<Button color="gray" onClick={() => navigate('/contact')}>
Contact Us
</Button>
EDIT: What makes this even stranger, is that If I remove the build step from my CI, and instead build manually but commit the build folder to my repo and have the CI just deploy build folder. Everything works fine. So ive narrowed down the issue to that it doesnt work if the build step is done in the CI
I was running into the same issue today after updating react-router
and react-router-dom
in my SPA
from version 6.10.0 to 6.12.1.
I just wanted to share some details of my constellation.
Running in a development environment (npm start
) works just fine.
Building manually and running the build either locally (npx serve -s build
) or on an external hoster yields the problem you describe:
navigation just does not work.
I use NavLink
and Navigate
elements and the useNavigate
function from react-router-dom
.
All types of navigation don't work.
I use BrowserRouter
.
I tried with the following node/npm-versions with the same effect:
Edit:
I just tested with which version the problem occurs:
It works with 6.11.2 and 6.12.0.
So it seems this problem was introduced with version 6.12.1.
Which version are you using?
Edit 2:
It seems the problem has been reported on GitHub already.