How to create dynamic route for top-level page in NEXT? - next.js

To support these URLs:
/account?tab=profile
/account?tab=pass
/account?tab=points
I know that I can change them to:
/account/profile
/account/pass
/account/points
And then create this route:
/pages/account/[tab].js
But this means that the accoun is a directory, not a file.
I want to have a account.js top-level file, and have a route for query strings on it.
I don't know how to do it. Something like /account?[tab] route. Is it possible?

import type { GetServerSideProps, NextPage } from 'next'
import Head from 'next/head'
import { ParsedUrlQuery } from 'querystring';
import styles from '../styles/Home.module.css'
const About: NextPage<{ query: ParsedUrlQuery }> = ({ query }) => {
console.log(query);
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<h1>About</h1>
</main>
</div>
)
}
export const getServerSideProps: GetServerSideProps = async ({ query }) => {
return {
props: {
query
}
}
}
export default About

Related

NextJS dynamic metatag without getServerSideProps

i want when u share a link from a topic in my forum it shows the title as the topic title, just like https://facebook.com/
that shows Facebook - log in or sign up
but i cant use
getServerSideProps
because i'm using html files to host (i need to) next build && next export -o
and its not supported on static html export:
https://nextjs.org/docs/advanced-features/static-html-export#unsupported-features
there is any other way to make it work?
i tried:
import React from "react";
import { useQuery } from "react-query";
import { fetchTopic } from "../../fetchers/forum";
import EmptyChangingPage from "../empty";
import Head from "next/head";
import { useRouter } from "next/router";
import { baseURL } from "../../baseUrl";
export default function Topic() {
const router = useRouter();
const { topicId } = router.query;
//
const {
data: topic,
isLoading: isLoadingTopic,
isFetched: isFetchedTopic,
} = useQuery([`topic:${topicId}`], () => fetchTopic(Number(topicId)), {
staleTime: 60000,
enabled: topicId !== undefined,
});
if (isLoadingTopic || !isFetchedTopic)
return <EmptyChangingPage />;
return (
<>
<Head>
<meta property="og:type" content="website" />
<meta
property="og:image"
content={`http://${baseURL}/imgs/og_logo.png`}
/>
<meta
property="og:url"
content={`http:/${baseURL}/forum/topic/?topicId=${topic?.topicId}`}
/>
<meta property="og:title" content={`Forum - ${topic?.title}`} />
<meta
property="og:description"
content={`content - ${topic?.message}`}
/>
</Head>
<main>
<div style={{ backgroundColor: "#dcdcdc" }}>
{/* forum stuff */}
</div>
</main>
</>
);
}
but when i send a link it shows
Forum - undefined
content - undefined

Google font loads very weirdly after the deployment to the vercel

I have deployed the next js app to the server using vercel. I have referenced the two google fonts in _document.js. While I am running the app locally both font load without any problem.
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document
{
static async getInitialProps(ctx)
{
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render()
{
return (
<Html>
<Head>
<link href="https://fonts.googleapis.com/css2?family=Crete+Round&family=Work+Sans:wght#500;600&display=swap" rel="stylesheet" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
index.js
import Head from "next/head";
import Script from "next/script";
import Banner from "../components/Banner";
import { fetchAPI } from "../lib/api";
import Articles from "../components/Articles";
export default function Home({ articles })
{
return (
<>
<Head>
<title>Life Sciencify - Explore the mystery of life with Science! </title>
</Head>
<Articles articles={articles} />
</>
);
}
export async function getServerSideProps()
{
const [articlesRes] = await Promise.all([
fetchAPI("/posts", { populate: ["cover", "category"] })
]);
console.log(articlesRes)
return {
props: {
articles: articlesRes.data
}
};
}
app.js
import Script from "next/script";
import "bootstrap/dist/css/bootstrap.css";
import "../styles/globals.css";
import { useEffect } from "react";
import Header from "../components/Header";
import SearchBlock from "../components/SearchBlock";
import Footer from "../components/Footer";
function MyApp({ Component, pageProps })
{
useEffect(() =>
{
import("bootstrap/dist/js/bootstrap");
}, []);
return (
<>
<Component {...pageProps} />
</>
);
}
export default MyApp;
After the deployment it is showing the weird behavior.
Initially When I am in the home page the page doesn't load any font.
Now, when I click the link Post1 or Post 2, it will be redirected to the detail page.
at first font is not loaded in this page too.
Now, after the page refresh the font gets loaded.
Now, when I go to the back page in the browser, the home page will have the font loaded. But again when the page is refreshed the font will be gone.
What is the causing the weird behavior?
I am running the application in the next js version of "12.1.6".
Referenced:
google-font-display
font-optimization
In the _document.js i used two google fonts separately and it is working now.
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document
{
static async getInitialProps(ctx)
{
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render()
{
return (
<Html>
<Head>
<link href="https://fonts.googleapis.com/css2?family=Work+Sans:wght#500;600&display=swap" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Crete+Round&display=swap" rel="stylesheet" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
and in index.js, change server side rendering to static props:
export async function getStaticProps()
{
const [articlesRes] = await Promise.all([
fetchAPI("/posts", { populate: ["cover", "category"] })
]);
console.log(articlesRes)
return {
props: {
articles: articlesRes.data
}
};
}
After this changes I deployed to vercel it worked fine, again after some time i changes to getServerSideProps, it was not working. So, the culprit was getServerSideProps with google font.

Next JS server side rendering on Index.tsx with NextPage componet

tsx file having a Home component below it there is getServerSiderProps method.i want to get server side but props inside my Home component show undefined.
import type { NextPage } from 'next'
import Head from 'next/head'
import Banner from '../Components/Banner'
import Header from '../Components/Header'
import Posts from '../Components/Posts'
import { sanityClient, urlFor } from '../sanity'
const Home: NextPage = ({ posts }) => {
console.log(posts)
return (
<div>
<Head>
<title>Medium Blog</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<Header></Header>
<Banner></Banner>
<Posts></Posts>
</div>
)
}
export default Home
//Server Side Rendering (SSR)
export const getServerSiderProps = async () => {
const query = `*[_type=="post"]{
_id,
title,
slug,
author->{
name,
image
},
description,
mainImage,
slug
}`
//Fetch
const posts = await sanityClient.fetch(query)
return { props: { posts } }
}
I want to get server side props every time it refresh my page but it became undefined.

Dynamic component imported stuck in loading phase : next js

I am new to next js. In my project I need to display youtube videos. I have an api which provides me the video ids to show with its meta details. I wanted to create dynamic pages for each videos. I am using react-player as player.
Here is my code
[videoId].tsx
import Head from 'next/head';
import { useRouter } from 'next/router'
import Layout from '../../components/layout';
import { IVideoItem } from '../../models/videos.model';
import VideoContainer from '../../components/videos-page/video-container';
import { getVideosPaths, getVideosPageTitle, getVideosPageDescription, getVideosData } from '../../services/videos-page.services';
export default function VideoPage({videoInfo} :IVideosPageProp) {
const router = useRouter()
if (router.isFallback) {
return <div>Loading...</div>
}
return(
<>
<Head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charSet="utf-8" />
<title>{getVideosPageTitle(videoInfo)}</title>
<meta name="description" content={getVideosPageDescription(videoInfo)} />
<meta property="og:title" content={getVideosPageTitle(videoInfo)} key="ogtitle" />
<meta property="og:description" content={getVideosPageDescription(videoInfo)} key="ogdesc" />
</Head>
<VideoContainer data={videoInfo} />
</>
)
}
export async function getStaticPaths() {
const paths = await getVideosPaths()
//console.log('paths: ',paths);
return {
paths,
fallback: false
}
}
export async function getStaticProps({ params }:IVideosPageStaticProp) {
const {videoId} = params;
const videoInfo = await getVideosData(videoId)
return {
props: {
videoInfo
}
}
}
interface IVideosPageProp {
videoInfo: IVideoItem
}
interface IVideosPageStaticPropParams {
videoId: string
}
interface IVideosPageStaticProp {
params: IVideosPageStaticPropParams
}
video-container.tsx
import { Row, Col } from 'react-bootstrap'
import { IVideoItem } from '../../models/videos.model';
import styles from './videos-container.module.scss';
import VideoTag from '../home/videos-block/video-tag';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faEye, faThumbsUp, faThumbsDown } from '#fortawesome/free-solid-svg-icons';
import moment from 'moment';
import dynamic from 'next/dynamic';
const ReactPlayer = dynamic(
() => import('react-player'),
{ loading: () => <p>...</p>, ssr: false }
)
export default function VideoContainer({data} :IVideosPageProp){
const videoInfo:IVideoItem = data;
const videoTag = [{"tagName": "Foo", "tagId": 1}]
const fallBackElement = () => {
return <img src={videoInfo.default_thumbnail_url} width="100%"/>
}
return (
<div className={styles['videos-container']}>
<ReactPlayer
url={`https://youtu.be/${data.video_id}`}
controls
width = "100%"
light={true}
playing={true}
fallback={fallBackElement()}
config={{
youtube: {
playerVars: { showinfo: 1 }
}
}}
/>
<div className={styles['videos-body']}>
<div className={styles['tag-list-container']}>
{videoTag.map((tag, index) =>{
return <VideoTag videoTag={tag} key={index}/>
})}
</div>
<div className={styles['video-title']}>
{videoInfo.title}
</div>
<Row className={styles['video-numbers']}>
<Col md={2} xs={2}><FontAwesomeIcon icon={faEye} className={styles['views-icon']} />{videoInfo.views_count}</Col>
<Col md={2} xs={4}>{moment(new Date(videoInfo.published_at)).format('Do MMMM YYYY')}</Col>
<Col md={4} xs={2}></Col>
<Col md={2} xs={2}><FontAwesomeIcon icon={faThumbsUp} className={styles['views-icon']} />{videoInfo.like_count}</Col>
<Col md={2} xs={2}><FontAwesomeIcon icon={faThumbsDown} className={styles['views-icon']} />{videoInfo.dislike_count}</Col>
</Row>
<div className={styles['video-description']}>
{videoInfo.description}
</div>
</div>
</div>
)
}
interface IVideosPageProp {
data:IVideoItem
}
When I run yarn dev the page is loading properly and the video player is rendering and working as expected. But when I run next buld and after that next start, the page is loading, but player is not loading. Insted it shows the "Loading..." message on the page, I refreshed several times, no luck. Not able to understand the issue. Can any one help?
Update 1:
The page is rendering with video title, video description etc. But the dynamically imported video player is not rendered. At the place of video player, it shows 'Loading...'.
Not sure if you can dynamically load from the node_module, like this:
const ReactPlayer = dynamic(
() => import('react-player'),
{ loading: () => <p>...</p>, ssr: false }
)
But you should be able to do this by creating a react-player component first, then dynamic import it like this:
// create a component named Player.js
import ReactPlayer from 'react-player';
const Player = props => (<ReactPlayer {...props}/>)
export default Player;
// then dynamic import it:
const Player = dynamic(
() => import('../components/Player'),
{ ssr: false }
)
// Then use <Player> with the same props

Ordering problem using css import alongside styled components in nextjs app

I am using NextJS and when I use direct css import ...css alongside a styled-component createGlobalStyle. My direct css imports are always included last in my html causing some overriding issues that I've setup in GlobalStyles.
How can I resolve this?
rendered html
<head>
<link href="/_next/static/chunks/main.js?ts=1609163560022">
<style></style> // global styled component
<style></style> // bootstrap imported after global styled component
</head>
_app.js
import { ThemeProvider } from "styled-components";
import 'bootstrap/dist/css/bootstrap.min.css' // this is always loaded last <head>
import GlobalStylesfrom 'styles/global'; // this is always loaded first in <head>
const theme = {
...
};
export default function App({ Component, pageProps }) {
return (
<ThemeProvider theme={theme}>
<GlobalStyles/>
<Component {...pageProps} />
</ThemeProvider>
)
}
_document.js
import Document, { Html, Head, Main, NextScript } from "next/document";
import { ServerStyleSheet } from "styled-components";
export default class MyDocument extends Document {
static getInitialProps({ renderPage }) {
const sheet = new ServerStyleSheet();
const page = renderPage(App => props =>
sheet.collectStyles(<App {...props} />)
);
const styleTags = sheet.getStyleElement();
return { ...page, styleTags };
}
render() {
return (
<Html>
<Head>{this.props.styleTags}</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}

Resources