Next JS Slug Page won work on vercel deployment - next.js

I made a website with NextJS and deployed it on Vercel.
From url: https://etiselektrik.vercel.app/teknikbilgi/ if i click the first article. The page loads without any problem.
However if i directly enter the url with the slug page: https://etiselektrik.vercel.app/teknikbilgi/reaktifenerji-reaktifceza I get 500 internal server error.
I dont have this error in my production build.
Here is my code for the slug page:
import ReactHtmlParser from 'react-html-parser';
import Layout from '../../components/Layout';
import { API_URL } from '../../config/index';
export default function BlogPost({ post }) {
return (
<Layout
title={post.title}
description={post.metaDescription}
keywords={post.metaKeywords}
>
<h1 className='px-4 py-4 md: my-4 simpleContainer font-semibold text-2xl text-secondary text-left'>
{post.title}
</h1>
<div className='px-4 simpleContainer text-secondary'>
{ReactHtmlParser(post.content)}
</div>
</Layout>
);
}
export async function getServerSideProps({ query: { slug } }) {
try {
const res = await fetch(`${API_URL}/teknikbilgis/${slug}`);
const post = await res.json();
return {
props: {
post: post,
},
};
} catch {
return {
props: {
post: {},
},
};
}
}

I found the issue. On the client side, i used .replaceAll function to change my post before parsing. (I didnt add them in my code as i thought it was irrelevant)
.replaceAll() seems to cause problems in nextjs on deployment. I changed it to .replace() Now it works.

Related

Nextjs Build fail on Vercel

I'm trying to deploy my NextJs app (using GraphCMS) on Vercel. When I build the app on my computer it works fine, I can build and run the app locally but once I try to deploy the same exact app on Vercel it crash with this error
TypeError: Cannot read properties of undefined (reading 'document')
at Object.parseRequestExtendedArgs (/vercel/path0/node_modules/graphql-request/dist/parseArgs.js:37:25)
at /vercel/path0/node_modules/graphql-request/dist/index.js:422:42
at step (/vercel/path0/node_modules/graphql-request/dist/index.js:63:23)
at Object.next (/vercel/path0/node_modules/graphql-request/dist/index.js:44:53)
at /vercel/path0/node_modules/graphql-request/dist/index.js:38:71
at new Promise ()
at __awaiter (/vercel/path0/node_modules/graphql-request/dist/index.js:34:12)
at request (/vercel/path0/node_modules/graphql-request/dist/index.js:418:12)
at getPosts (/vercel/path0/.next/server/chunks/104.js:1143:82)
at getStaticPaths (/vercel/path0/.next/server/pages/post/[slug].js:98:86)
Build error occurred
Error: Failed to collect page data for /post/[slug]
at /vercel/path0/node_modules/next/dist/build/utils.js:959:15
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
type: 'Error'
}
error Command failed with exit code 1.
I don't understand where this is coming from.
pages/post/[slug].js
import React from "react";
import { useRouter } from "next/router";
import {
PostDetail,
Categories,
PostWidget,
Author,
Comments,
CommentsForm,
Loader,
} from "../../components";
import { getPosts, getPostDetails } from "../../services";
import { AdjacentPosts } from "../../sections";
const PostDetails = ({ post }) => {
const router = useRouter();
if (router.isFallback) {
return <Loader />;
}
return (
<>
<div className="container mx-auto px-10 mb-8">
<div className="grid grid-cols-1 lg:grid-cols-12 gap-12">
<div className="col-span-1 lg:col-span-8">
<PostDetail post={post} />
<Author author={post.author} />
<AdjacentPosts slug={post.slug} createdAt={post.createdAt} />
<CommentsForm slug={post.slug} />
<Comments slug={post.slug} />
</div>
<div className="col-span-1 lg:col-span-4">
<div className="relative lg:sticky top-8">
<PostWidget
slug={post.slug}
categories={post.categories.map((category) => category.slug)}
/>
<Categories />
</div>
</div>
</div>
</div>
</>
);
};
export default PostDetails;
// Fetch data at build time
export async function getStaticProps({ params }) {
const data = await getPostDetails(params.slug);
return {
props: {
post: data,
},
};
}
// Specify dynamic routes to pre-render pages based on data.
// The HTML is generated at build time and will be reused on each request.
export async function getStaticPaths() {
const posts = await getPosts();
return {
paths: posts.map(({ node: { slug } }) => ({ params: { slug } })),
fallback: false,
};
}
here is the Graphql query getPosts
export const getPosts = async () => {
const query = gql`
query MyQuery {
postsConnection {
edges {
cursor
node {
author {
bio
name
id
photo {
url
}
}
createdAt
slug
title
excerpt
displayedDate
featuredImage {
url
}
categories {
name
slug
}
}
}
}
}
`;
const result = await request(graphqlAPI, query);
return result.postsConnection.edges;
};
getPostDetails
export const getPostDetails = async (slug) => {
const query = gql`
query GetPostDetails($slug: String!) {
post(where: { slug: $slug }) {
title
excerpt
featuredImage {
url
id
}
author {
name
bio
photo {
url
}
}
createdAt
slug
content {
raw
}
categories {
name
slug
}
displayedDate
}
}
`;
const result = await request(graphqlAPI, query, { slug });
return result.post;
};
I really don't understand why I can build it locally but not en Vercel, Thanks
Tried to modify queries, turn off fallback and others things that did not work

-Fallback: true causes meta tags not to be in source

A Copy of an issue I opened - Seeing a strange behaviour when using fallback: true, have followed all the documentation regarding fetching data int he getStaticProps method and even using hardcoded value the result is the same. When using fallback: true, locally when the page loads the meta description and title appear in the elements of the console, but inspecting the source they are not present. Where as using fallback: 'blocking' the meta description and title is present in both elements and the source. On our deployed version, the behaviour is slightly different, the first time the page is generated, the meta description and title are missing from the source, however refreshing the page they then become present. Im not 100% if this is an issue or just the way it was working but our SEO team are complaining that title and meta descriptions are missing, testing this with a variety for online crawlers, some seem to pick up the title and description without any problems and others say they are missing
forked an app and created an example app where this can be clearly seen selected the fallback: true page and the title and description are missing, use the fallback: blocking route and both can be found in the source as expected.
Has anyone encountered the same, does this actually affect the seo scoring?
example of page using blocking method:
import Link from "next/link";
import Head from "next/head";
import { fetchImportantProductIds, fetchProduct } from "../../api";
export default function NonBlockingProduct({ product }) {
return (
<>
<Head>
<title>{product.id}</title>
<meta name="description" content={product.description} />
</Head>
<div>
<h1>Product number {product.id}</h1>
<p>{product.description}</p>
<div style={{ marginTop: "1rem" }}>
<Link href="/">
<a>Home</a>
</Link>
</div>
</div>
</>
);
}
export async function getStaticPaths() {
const importantProductIds = await fetchImportantProductIds();
return {
paths: importantProductIds.map((id) => ({ params: { id } })),
fallback: "blocking"
};
}
export async function getStaticProps({ params }) {
const product = await fetchProduct(params.id);
return { props: { product } };
}
example of non-blocking option :
import { useRouter } from "next/router";
import Head from "next/head";
import Link from "next/link";
import { fetchImportantProductIds, fetchProduct } from "../../api";
export default function NonBlockingProduct({ product }) {
const router = useRouter();
if (router.isFallback) {
return "Loading...";
}
return (
<>
<Head>
<title>{product.id}</title>
<meta name="description" content={product.description} />
</Head>
<div>
<h1>Product number {product.id}</h1>
<p>{product.description}</p>
<div style={{ marginTop: "1rem" }}>
<Link href="/">
<a>Home</a>
</Link>
</div>
</div>
</>
);
}
export async function getStaticPaths() {
const importantProductIds = await fetchImportantProductIds();
return {
paths: importantProductIds.map((id) => ({ params: { id } })),
fallback: true
};
}
export async function getStaticProps({ params }) {
const product = await fetchProduct(params.id);
return { props: { product } };
}
I had an answer back from next that the behaviour is expected and that for crawlers they will receive the page the same as using the blocking method
This is expected behavior. fallback: true serves a statically generated If you want all requests to hold the fully generated HTML you can use fallback: 'blocking'. Crawlers get the fallback: 'blocking' behavior in all cases. Please see the documentation here: https://nextjs.org/docs/api-reference/data-fetching/get-static-paths#fallback-true

Next.js hydration error using Keycloak SSR package

I am using #react-keycloak/ssr with latest next.js, just started so project as clean as possible, all I really have are installed dependencies and _app.tsx with index.tsx from examples.
_app.tsx is identical copy (except url to keycloak) of official github example and index.tsx is next:
import { useKeycloak } from '#react-keycloak/ssr'
import {KeycloakInstance, KeycloakTokenParsed} from 'keycloak-js'
type ParsedToken = KeycloakTokenParsed & {
email?: string
username?: string
}
export default function Index() {
const { keycloak } = useKeycloak<KeycloakInstance>()
const parsedToken: ParsedToken | undefined = keycloak?.tokenParsed
const state = keycloak?.authenticated ? <span>{parsedToken?.username}</span> : <span>'Undefined'</span>;
function handleLoginButtonClick() {
if (keycloak) {
window.location.href = keycloak.createLoginUrl()
}
}
return (
<div>
{state}
<button className="btn btn-blue" onClick={() => handleLoginButtonClick()}>Login</button>
</div>
)
}
And my problem is that after a login I am getting errors
Warning: Text content did not match. Server: "" Client: "'Undefined'"
at span
at div
at Index (webpack-internal:///./pages/index.tsx:18:84)
I've tried to implement state change using useEffect but then keycloak?.authenticated is always false,
let [state] = useState('No user');
useEffect(() => {
state = keycloak?.authenticated ? 'User' : 'No user';
}, []);
then I tried to use getServerSideProps, but then I get an error that useKeycloak hook can be used inside a function only.
What else can I try?
p.s. Short gif/video of what is happening https://imgur.com/a/c2q6ftU
Found a solution by tweaking useEffect slightly:
const [username, setUsername] = useState('unknown')
useEffect(() => {
if (keycloak?.authenticated) {
setUsername(parsedToken?.email)
}
}, [keycloak?.authenticated])

Next.js getStaticProps/getServerSideProps best use case

I have a blog that is using getStaticProps + getStaticPaths to generate pages from headless WP - all good. Then I have a component that is connecting to the database to allow me to link to the next post in any within any individual category. This component is then used inside the ...postSlug page.
So basically the component works perfectly if I switch to using getServerSideProps for posts, as it updates the 'previous' + 'next' URLs as each post is requested and built on the server. However, if I use getStaticProps the component doesn't update with the correct URLs unless I manually refresh the page. So I need to use some client-side data fetching within the component, with useState, useEffect or SWR or something - but I'm not sure about the best way to do it (or even if that would work or if I should just use gServerSideProps).
Any help would be greatly appreciated. Thanks! Component below edited for clarity ...
export default function MovePost() {
const { usePost } = client
const post = usePost()
const { query = {} } = useRouter()
const { categorySlug } = query
const currentPaginationCursor = btoa( `arrayconnection:${post.databaseId}` )
const { posts } = client.useQuery()
const previous = posts({
first: 1,
before: currentPaginationCursor,
where: { categoryName: `${categorySlug}` },
}).nodes[0]?.slug
return (
<>
{ previous &&
<Link href={`/archive/${categorySlug}/${previous}`}>
<a ...
export default function Page() {
const { usePost } = client
const post = usePost()
return (
<>
<Layout>
<div dangerouslySetInnerHTML={{ __html: post?.content() ?? '' }} />
<MovePost />
</Layout>
</>
)
}
export async function getStaticProps(context: GetStaticPropsContext) {
return getNextStaticProps(context, {
Page,
client,
notFound: await is404(context, { client }),
})
}
export function getStaticPaths() {
return {
paths: [],
fallback: 'blocking',
}
}

Next JS + Next Auth Protected page loads for seconds after redirection

I am trying to redirect to a protected page in the following way:
<button
onClick={() => {
router.push("/welcome");
}}
>
The welcome page is protected in the following way:
import { useSession, signOut } from "next-auth/client";
import { useRouter } from "next/router";
import { useEffect } from "react";
export default function Welcome() {
const [session, loading] = useSession();
const router = useRouter();
useEffect(() => {
if (!loading && !session?.accessToken) {
router.push("/auth/login");
}
}, [loading, session]);
if (!session) {
return <div></div>;
}
return (
<div>
<h1>Welcome Page</h1>
</div>
);
}
The welcome page works fine if I try to access it directly.
But if I use router.push(), it takes seconds to load. It mostly happens after going back from the welcome page with the browser's back button and then pressing the button again. Though it sometimes happens anyways.
What am I doing wrong? I was told it's normal behaviour for a dev environment, but it happens in production build too.
Here is a reproduction:
https://codesandbox.io/s/distracted-browser-fyjek
I solved the issue by wrapping my Component in _app.tsx with Provider like so:
<Provider session={pageProps.session}>
<Component {...pageProps} />
</Provider>;
and adding
export async function getServerSideProps(context: NextPageContext) {
const session = await getSession(context);
return {
props: { session },
};
}
to server side rendered pages like explained here.

Resources