1

I want to use a multi-step form with Next JS but to do so I must add the inside the app.js file which looks like this:

import React from "react";
import ReactDOM from "react-dom";
import App from "next/app";
import Head from "next/head";
import Router from "next/router";
import { SessionProvider } from "next-auth/react";
import StepContext from "./step_context.js";
import PageChange from "components/PageChange/PageChange.js";
import "@fortawesome/fontawesome-free/css/all.min.css";
import "styles/tailwind.css";
import { BrowserRouter, Routes, Route } from "react-router-dom";

Router.events.on("routeChangeStart", (url) => {
  console.log(`Loading: ${url}`);
  document.body.classList.add("body-page-transition");
  ReactDOM.render(
    <PageChange path={url} />,
    document.getElementById("page-transition")
  );
});
Router.events.on("routeChangeComplete", () => {
  ReactDOM.unmountComponentAtNode(document.getElementById("page-transition"));
  document.body.classList.remove("body-page-transition");
});
Router.events.on("routeChangeError", () => {
  ReactDOM.unmountComponentAtNode(document.getElementById("page-transition"));
  document.body.classList.remove("body-page-transition");
});

export default class MyApp extends App {
  componentDidMount() {
    let comment = document.createComment(``);
    document.insertBefore(comment, document.documentElement);
  }
  static async getInitialProps({ Component, router, ctx }) {
    let pageProps = {};

    if (Component.getInitialProps) {
      pageProps = await Component.getInitialProps(ctx);
    }

    return { pageProps };
  }

  render() {
    const { Component, pageProps, session } = this.props;

    const Layout = Component.layout || (({ children }) => <>{children}</>);

    return (
      <>
        <StepContext>
          <React.Fragment>
            <Head>
              <meta
                name="viewport"
                content="width=device-width, initial-scale=1, shrink-to-fit=no"
              />

              <title>Futur Banking</title>
            </Head>
            <Layout>
              <Component {...pageProps} />
            </Layout>
          </React.Fragment>
        </StepContext>
      </>
    );
  }
}

I have to wrap anything inside return statement with tag to use the multi-step form, but this makes the web show the multi-step page form on every url.

If i change the code and put it inside the component it shows the form added below every page.

How can I fix this issue and show the multi-step form only inside the specific url not on every page.

altini
  • 11
  • 2

1 Answers1

0

Anything on _app.js or _app.tsx is for the entire app. It will always occur on every single page of the entire app. There is nothing you can do on this page to avoid that.

This file should be very bare. You can put a loading animation here, but this is something meant for every URL. I am not sure of anything else you would want in here really.

Next uses files in the pages directory to create URL routes for you. So you will name the file what you want to see in the URL.

Also, I like to make my layout a component.

/my-app
├── pages/
│   ├── api/
│   ├── _app.js 
│   ├── _document.js 
│   ├── index.js 
│   └── quiz.js // Name this what you want in the URL
└── components/
    └── stepcontext.js
    └── layout.js

OR

Look into the new app router upgrade from page routes.

Here is what your Quiz page will look like using your StepContext component:

import StepContext from './components/stepcontext'
import Layout from './components/layout'

export default function Quiz() {
  return (
    <Layout title="Quiz | Futur Banking">
      <StepContext />
    </Layout>
  )
}

Here is your layout component for dynamic page titles:

const Layout = ({ children, title = "Futur Banking" }: Props) => {
  ...
}

This will give you the multistep form on only one URL: localhost:3000/quiz. Make sure to remove StepContext from _app.js also.

To manually handle routes in _app.js, you could try something more like this, treating the pages like components to clean this up a bit but not needed. Simply add your css and classes to the components and pages directly.

import Quiz from './quiz'
import { useRouter } from 'next/router';
  ...
  const router = useRouter();

  if (router.pathname === '/quiz') {
    return <Quiz {...pageProps} />
  }

  return (
    <> or <React.fragment>
    <Head>
      <meta
        name="viewport"
        content="width=device-width, initial-scale=1, shrink-to-fit=no"
      />

      <title>{title}</title>
    </Head>
    <Component {...pageProps} />
    </> or </React.fragment>
  )