Programmatically navigate to a dynamic url with gatsby - reactjs

I am using navigate to move to another URL. I saw many posts using Link to move to another page with dynamic url. But I want to change url without writing jsx
When I navigate to the following url, I get a 404 error
navigate(`/vidx/${u}`, {
state: { vid: r }
})
I changed gatsby-node.js to following, still getting the same error. I have a file named vidx.js in pages folder
exports.onCreatePage = async ({ page, actions }) => {
const { createPage } = actions
if (page.path.match(/^\/vidx/)) {
page.matchPath = "/vidx/*";
createPage(page);
}
}
My url will look like this - www.xyz.com/vidx/123456789. The number 123456789 will depend upon the user logged in

I want to redirect to vidx.js; but the URL should be /vidx/123456789
That will never work since /vidx/123456789 will always throw a 404 error since it's not generated and doesn't exist, it's a separate new page.
One easy thing you can do use: /vidx?queryParameter=123456789. In that case, your page will remain being /vidx and you can get the queryParameter to make your stuff with your own logic.

Related

Can I redirect in Next.js passing data?

I want to be able to redirect to another page in Next.js passing data. The reason about why I want to do it is the following:
I am working on a project in which the user can be an entity or not. If it is not an entity the page about the user will be / and if it is an entity the page about the user will be /entity.
When I go to the page / I use getServerSideProps to fetch all the data about the user and send it as a prop. However, if the fetched user is an entity I redirect to /entity. Then, I use getServerSideProps in /entity to fetch all the data about the user (that is an entity).
I am doing two requests when I only should do one of them. If I manage to redirect to /entity passing the data that I already fetched I wouldn't have this problem. Is there a way I can do it?
// index.js
export async function getServerSideProps(context) {
const user = await getUser(accessCookies(context));
if (user.isEntity)
return { redirect: { destination: "/entity", permanent: false } }; // Would like to send user
return { props: { user} };
}
I don't see any way to achieve it and I don't even know if it is possible.

Can I use Next.js Link to navigate from slug page to the default one?

So I have the default page and some 'slug' dynamic pages, the urls look like this:
default page locally: localhost:3000/doc/default
slug page locally: localhost:3000/doc/[slug]
default page in production: default.com/doc
slug page in production: [slug].default.com/doc
The link I need should be inside the slug pages and lead to the default one. So far I tried getting the basePath from nextRouter to use as a href. But that returns the slug url ([slug].default.com/doc.
Can I use the Next.js Link component and point it to /doc/default? It works locally, but I don't want to push it to dev/prod just to test if it works.
Have you tried redirects?
Next.js has an experimental a feature to redirect pages ahead of time inside its next.config.js file.
Let's say we'd like to redirect our root path site.com/ to site.com/en-us/. We could use useRouter whenever we needed to redirect the user or we could add directly into next.config.js:
const nextConfig = {
async redirects() {
return [
{
source: '/',
permanent: false,
destination: '/en-us',
},
]
}
}
Now anytime the user navigates to site.com/ he'll be redirected to site.com/en-us/ which will render the page inside pages/en-us/index.(jsx|tsx|mdx). It is also possible to use RegEx for matching as described on this issue.
For your use case, would be the the other way around. We'd probably have to use absolute paths, since we're redirecting to another domain. In this case, a subdomain of our root default.com domain.
// const getSlugs = fs.readdir(path.resolve(...), () => {})
const slugs = ['slug1', 'slug2']
const slugRedirects = slugs.map(slug => ({
source: `/doc/${slug}`,
destination: `https://${slug}.default.com/doc`,
permanent: false
}))
const nextConfig = {
async redirects () {
return [...slugRedirects]
}
}
module.exports = nextConfig
Now, whenever the user navigates to www.default.com/doc/slug1, either by typing the URL or clicking on a button that points to the pages/doc/[slug] pages, he will be redirected to slug1.default.com/doc.
Let me know if that helps. Cheers!

Prevent flash of wrong page in NextJS app after MSAL-React redirect to/from Azure AD B2C

Context & Reproducible Scenario
I'm using the combination of these libraries and tools:
NextJS 12+ (based on React 18+)
MSAL-Browser 2.25+ and MSAL-React 1.6+ (Microsoft's libs for OpenID login against Azure B2C)
I'm using the Auth Code + PKCE redirect flow so this is the flow for users:
They land on /, the home page
They click a /me router link
They go to Azure B2C to log in because said page has this logic:
<MsalAuthenticationTemplate
interactionType={InteractionType.Redirect}
authenticationRequest={loginRequest}>
where loginRequest.state is set to router.asPath (the "intended" page: /me)
Note that the page is also wrapped in a <NoSsr> component based off Stack Overflow.
User logs in on Azure B2C, gets redirected back to my app at / (the root)
⛔ Problem: the user now briefly sees the / (home) page
After a very brief moment, the user gets sent to /me where they are signed in
The MSAL docs don't seem to have much on the state property from OIDC or this redirect behavior, and I can't find much about this in the MSAL sample for NextJS either.
In short: the issue
How do I make sure MSAL-React in my NextJS application send users to the "intended" page immediately on startup, without briefly showing the root page where the Identity Server redirects to?
Relevant extra information
Here's my custom _app.js component, which seems relevant because it is a component that triggers handleRedirectPromise which causes the redirect to intended page:
export default function MyApp({ Component, pageProps }) {
return (
<MsalProvider instance={msalInstance}>
<PageHeader></PageHeader>
<Component {...pageProps} />
</MsalProvider>
);
}
PS. To help folks searching online find this question: the behavior is triggered by navigateToLoginRequestUrl: true (is the default) in the configuration. Setting it to false plainly disables sending the user to the intended page at all.
Attempted solutions with middleware
I figured based on how APP_INITIALIZERs work in Angular, to use middleware like this at some point:
// From another file:
// export const msalInstance = new PublicClientApplication(msalConfig);
export async function middleware(_request) {
const targetUrlAfterLoginRedirect = await msalInstance.handleRedirectPromise()
.then((result) => {
if (!!result && !!result.state) {
return result.state;
}
return null;
});
console.log('Found intended target before login flow: ', targetUrlAfterLoginRedirect);
// TODO: Send user to the intended page with router.
}
However, this logs on the server's console:
Found intended target before login flow: null
So it seems middleware is too early for msal-react to cope with? Shame, because middleware would've been perfect, to allow as much SSR for target pages as possible.
It's not an option to change the redirect URL on B2C's side, because I'll be constantly adding new routes to my app that need this behavior.
Note that I also tried to use middleware to just sniff out the state myself, but since the middleware runs on Node it won't have access to the hash fragment.
Animated GIF showing the flashing home page
Here's an animated gif that shows the /home page is briefly (200ms or so) shown before /me is properly opened. Warning, gif is a wee bit flashy so in a spoiler tag:
Attempted solution with custom NavigationClient
I've tried adding a custom NavigationClient to more closely mimic the nextjs sample from Microsoft's repository, like this:
import { NavigationClient } from "#azure/msal-browser";
// See: https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-react/docs/performance.md#how-to-configure-azuremsal-react-to-use-your-routers-navigate-function-for-client-side-navigation
export class CustomNavigationClient extends NavigationClient {
constructor(router) {
super();
this.router = router;
}
async navigateInternal(url, options) {
console.log('👍 Navigating Internal to', url);
const relativePath = url.replace(window.location.origin, "");
if (options.noHistory) {
this.router.replace(relativePath);
} else {
this.router.push(relativePath);
}
return false;
}
}
This did not solve the issue. The console.log is there allowing me to confirm this code is not run on the server, as the Node logs don't show it.
Attempted solution: go through MSAL's SSR docs
Another thing I've tried is going through the documentation claiming #azure/msal-react supports Server Side Rendering (SSR) but those docs nor the linked samples demonstrate how to solve my issue.
Attempted solution in _app.tsx
Another workaround I considered was to sniff out the hash fragment client side when the user returns to my app (and make sure the intended page is also in that state). I can successfully send the OpenID state to B2C like this...
const extendedAuthenticationRequest = {
...authenticationRequest,
state: `~path~${asPath}~path~`,
};
...and see it returned in the Network tab of the dev tools.
However, when I try to extract it in my _app.tsx still doesn't work. I tried this code from another Stack Overflow answer to get the .hash:
const [isMounted, setMounted] = useState(false);
useEffect(() => {
if (isMounted) {
console.log('====> saw the following hash', window.location.hash);
const matches = /~path~(.+)~path~/.exec(window.location.hash);
if (matches && matches.length > 0 && matches[1]) {
const targetUrlAfterOpenIdRedirect = decodeURIComponent(matches[1]);
console.log("Routing to", targetUrlAfterOpenIdRedirect);
router.replace(targetUrlAfterOpenIdRedirect);
}
} else {
setMounted(true);
}
}, [isMounted]);
if (!isMounted) return null;
// else: render <MsalProvider> and the intended page component
This does find the intended page from the state and executes routing, but still flashes the /home page before going to the intended page.
Footnote: related GitHub issue
Submitted an issue at MSAL's GitHub repository too.

How to have dynamic redirect URLs in Next.js?

I'm working on a Next.js/Django project, which the user is able to add some redirect logic from the admin panel like:
[
{ source: "/about", destination: "google.com" },
{ source: "/about1", destination: "google1.com" },
{ source: "/about2", destination: "google2.com" },
]
and the web application should be able to handle these dynamic redirects.
As the Nextjs docs says, we can do this in next.config.js. The problem is that we can't have dynamic data in next.config.js. With every change in this file, server must be restarted.
Here we need a logic that gets the urls using an API on website load, loops through them and listens for every route calls to see if they match the redirect data or not.
I have tried some other ways too, like trying to use useEffect, but this way causes the website to render 404 page first and then it redirects to the desired url, which is not that nice for user experience viewpoints.
You can use Next.js Middleware to fetch the dynamic redirects array from the API, and add your own logic to handle the redirection.
Unlike redirects in the next.config.js that run at build time, Next.js Middleware runs on each incoming request to your pages and is able to dynamically fetch the redirects every time.
export async function middleware(req) {
// Fetch redirects array from API
const res = await fetch('https://example.com/api/redirects');
const redirects = await res.json();
/* Assuming the returned `redirects` array would have the following structure:
[
{ source: '/about-us', destination: '/about' },
{ source: '/google', destination: 'https://google.com' }
]
*/
// Find the matching redirect object
const redirect = redirects.find((redirect) => req.nextUrl.pathname === redirect.source);
if (redirect) {
if (redirect.destination.startsWith('http')) {
// Handle external URL redirects
return NextResponse.redirect(new URL(redirect.destination));
}
// Handle internal URL redirects
return NextResponse.redirect(new URL(redirect.destination, req.url));
}
// Continue if no matching redirect is found
return NextResponse.next();
}

Next.js How to make a redirect correctly

I have such a page structure.
locale can be any value from the language-country pair
eg en-ca, fr-fr, ar-en
[locale] // main folder route
page1.tsx // child route
page2.tsx // child route
page3.tsx // child route
For example, if I go to the address /page1, then I redirect to /locale/page1
But the problem is that I check on the browser side and first a 404 page appears in the browser and only then redirects to the correct address.
I think that it is necessary to check on the server.
I was making my own file for the server. But the problem is that there is no way to track the address for which the request is going and that means I cannot check if the address starts with the parameter I need.
In case you do not wont to use Next.js native i18n routing you can create fallback page file pages/[...fallback].ts to catch all non-existing page. Then you can use use getServerSideProps to redirect properly.
This could work for you (not tested):
// The page content will never be rendered
const FallbackPage = () => <div>Redirecting...</div>
// Is processed on every request on server side
export const getServerSideProps: GetServerSideProps = async (ctx) => {
// TODO: add custom detection (e.g. based on headers or cookies)
const locale = 'en'
const destination = `/${locale}/${ctx.params.fallback.join('/')}`
return {
props: {},
redirect: { destination, permanent: false },
}
}
export default FallbackPage

Resources