Shreyas Jadhav
Shreyas Jadhav

Reputation: 2571

Next.js Opt out of Layout Component for specific pages from _app.js

How to not wrap a specific page with Layout component in _app.js?

For example, I have two pages as pages/home and pages/about, now how can I not wrap my pages/home page with Layout component?

pages/_app.js

import "../styles/globals.css";
import Layout from "../components/Layout";

function MyApp({ Component, pageProps }) {

      return (
        <Layout>
          <Component {...pageProps} />
        </Layout>
      );
  
}

export default MyApp;

What I have tried:

pages/_app.js

function MyApp({ Component, pageProps }) {
  console.log(typeof Component); // gives me a function

  switch (Component) {
    case Home():
      return <Component {...pageProps} />;
    default:
      return (
        <Layout>
          <Component {...pageProps} />{" "}
        </Layout>
      );
  }
}

pages/home.js

import React from 'react';
 
const Home= () => {
  return (<div>Hello</div>);
};
 
export default Home;

Upvotes: 33

Views: 37184

Answers (8)

Shreyas Jadhav
Shreyas Jadhav

Reputation: 2571

by checking the appProps.router.pathname property passed to it.

way 1

function MyApp({ Component, pageProps, ...appProps }: AppProps) {

  // make function that will return the children based on router.pathname

  const getContent = () => {
    // array of all the paths that doesn't need layout
    if ([`/dashboard`].includes(appProps.router.pathname))
      return <Component {...pageProps} />;

    return (
      <Layout>
        <Component {...pageProps} />{" "}
      </Layout>
    );
  };
   

  return <ApplicationWrapper>{getContent()}</ApplicationWrapper>;
}

way 2

function MyApp({ Component, pageProps, ...appProps }: AppProps) {
  
  // use a LayoutComponent variable 
  // that switches to actual Layout or React.Fragment (no layout) 
  // accordingly to pathname

  const isLayoutNeeded = [`/dashboard`].includes(appProps.router.pathname);
  const LayoutComponent = isLayoutNeeded ? Layout : React.Fragment;

  return (
    <ApplicationWrapper> 
      <LayoutComponent>
        <Component />
      </LayoutCompnent>
    </ApplicationWrapper>
  );
}

TIP:

you can use path.startsWith to check all the paths, example

if(router.pathname.startsWith(`/dashboard`))

Upvotes: 43

Mike Rehy
Mike Rehy

Reputation: 17

When i use

class MyProfile extends React.Component {

    static noLayout = true;

    constructor(props){
        super(props);


    }
    render()
    {
        return (
            <a href="/test">test </a>

    )
}


 export default withRouter (connect(mapStateToProps, mapDispatchToProps)(MyProfile));

The static Variable noLayout is not accesible in _app.jsx

Component.noLayout.  // is undefined

Is undefined.

No way to "inject" static variable or props to determine which layout to use.

(maybe i can only use the router object)
When i look into:

console.log("Component:", Component);
console.log("Component props :", this.props);
console.log("Component page props :", pageProps);

enter image description here

So only what i see is this.props.router

if (this.props.router.pathname == "/ja/profil")
{
    return <Component {...pageProps} />;
}

Is there any better way?

Upvotes: -1

W.A.K.Jithmini
W.A.K.Jithmini

Reputation: 21

I have tried my code in this way and its working fine for me.

`
    import { useRouter } from "next/router";
    function MyApp({ Component, pageProps}: AppProps) {
      const router = useRouter();
      return (
        <>
          {router.pathname !== "/contact" ? (
            <>
              <NavBar />
              <Component {...pageProps} />
              <JoinUsSection />
              <Footer />
            </>
          ) : (
            <>
              <NavBar />
              <Component {...pageProps} />
              <Footer />
            </>
          )}
        </>
      );

}`

Upvotes: 1

newbie
newbie

Reputation: 146

What about using higher order components . They are not part of react API but as react docs says, "They are a pattern that emerges from React’s compositional nature." Next uses react so it does make sense to use react patterns in next

The following code wraps a given component with a predefined Header and Footer component. Then a component that uses it is wrapped with the HOC when exported

    const withLayout = Comp =>  {
        const WrappedComp = (props) => {
            return (
                <div id='layout'>
                    <Header />
                    <Comp {...props} />
                    <Footer />
                </div>
            );
        }
        return WrappedComp;
    }

    const Section = () => {
        return ( 
            <section>
                Section content...
            </section>
        );
    }

    export default withLayout(Section);

Upvotes: 1

Tushar Hasan
Tushar Hasan

Reputation: 55

You can simply leverage useRouter from 'next/router' and get your job done easily.

import {useRouter} from 'next/router';

function MyApp({ Component, pageProps }) {
  const router = useRouter();

  if(router.asPath =='/dashboard')  {
     return (
       <Component {...pageProps} />
     )
  }

 return (
   <Layout>
     <Component {...pageProps} />
   </Layout>
 );
 }

Upvotes: 2

Aramen Meza
Aramen Meza

Reputation: 31

what about this? Hope can save someone

import "../styles/globals.css";
import dynamic from "next/dynamic";
const Layout = dynamic(() => import("@layout/Layout"));
import { useRouter } from "next/router";

function MyApp({ Component, pageProps }) {
  const router = useRouter();
  return (
    <>
      {router.pathname !== "/" ? (
        <Layout>
          <Component {...pageProps} />
        </Layout>
      ) : (
        <Component {...pageProps} />
      )}
    </>
  );
}

export default MyApp;

Upvotes: 3

Ivan Nakov
Ivan Nakov

Reputation: 444

I think there is cleaner way of doing this with Per-Page Layouts. I'm currently doing this simple by creating a default layout for all pages and override it for the pages that require specific layout, for example in my login and registration pages.

    export default function LoginPage() {
      return {
        /** Some JSX */
      }
    }
    // Return the page without additional layout.
    LoginPage.getLayout = (page) => page

    export default function MyApp({ Component, pageProps }) {
      // Use the specified page layout or fallback to the default one.
      const getLayout = Component.getLayout ?? defaultPageLayout

      return getLayout(<Component {...pageProps} />)
    }

Upvotes: 30

deadcoder0904
deadcoder0904

Reputation: 8693

I use displayName static property. It works in any React.js component as well.

const OGImagePreview = () => <h1>OG Image Preview</h1>

OGImagePreview.displayName = 'OGImagePreview'

export default OGImagePreview

Then I use switch...case in _app.tsx like:

switch (Component.displayName) {
    case 'OGImagePreview':
        return (
            <>
                <Component {...pageProps} />
            </>
        )
    default:
        return (
            <>
                <Head>
                    <meta name="viewport" content="initial-scale=1.0, width=device-width" />
                </Head>
                <ThemeProvider attribute="class" themes={['light', 'dark']}>
                    <Progress />
                    <Nav />
                    <Component {...pageProps} />
                </ThemeProvider>
                <ScrollToTop />
                <Analytics />
            </>
        )
}

Upvotes: 2

Related Questions