13

Long story short is I'm working on a project where I want to have the content "fill" the vertical space below the static header. I've done this in React with tailwind like this:

<body class="flex flex-col h-screen text-gray-600 work-sans leading-normal text-base tracking-normal">
    <header class="flex h-18 bg-white shadow-md">
        {/* header menu options */}
    </header>
    <div class="flex flex-1 h-full bg-gray-200 p-6">
        {/* page content */}
    </div>

But with NextJS it seems to put the mounting div (i.e. <div id="__next">) between the body and the wrest of the content. If I modify the CSS to give #__next { height: %100 } but that makes the fill not work correctly, it overflows. So it looks like this:

<body class="flex flex-col h-screen text-gray-600 work-sans leading-normal text-base tracking-normal">
    <div id="__next">
        <header class="flex h-18 bg-white shadow-md">
            {/* header menu options */}
        </header>
        <div class="flex flex-1 h-full bg-gray-200 p-6">
            {/* page content */}
        </div>
    </div>

Here are screenshots to visually see why the extra div is causing problems: https://i.stack.imgur.com/xZloH.jpg

The two possible options to solve this problem that theoretically might work are add classes to the #__next div or mount to body instead of the #__next div. Does anyone know how to achieve either of those?

Edit: Yes, I think I could change the layout to a fixed header and padding on top of the content element and that'd sidestep the problem and that may end up being the workaround I need but I'm still interested in knowing if either of the solutions I've mentioned are possible because if they aren't that's a technical limitation of NextJS that doesn't get much attention.

Penny Liu
  • 15,447
  • 5
  • 79
  • 98
Dillan Wilding
  • 1,030
  • 2
  • 12
  • 28
  • set __next height 100% and box-sizing: border-box; padding and margin on 0 – Robert Dec 19 '20 at 22:11
  • and body, html height 100% – Robert Dec 19 '20 at 22:14
  • @Robert The result is the same as the overflow picture I posted to imgur – Dillan Wilding Dec 20 '20 at 00:30
  • your header is fixed height. h-18, so just calc(100% - 18px) should be fine, or put this layer as absolute top 0 and min-height 100% should work as well. (but in second case you have to change zindex of header) – Robert Dec 20 '20 at 01:16
  • or better. if header is always on top just make it fixed mean position: fixed and top: 0. this will remove it from __nav and height of 100 % will treated as 100% of page not plus height of header – Robert Dec 20 '20 at 01:32
  • I've never seen calc(100% - 18px). I'll have to give it a try. That's a good point, I could change the way I put the navigation header at the top but then I'd have to compensate by adding a top padding to the content space. Thanks for your suggestions! I'll report back. – Dillan Wilding Dec 20 '20 at 01:48

4 Answers4

12

I resolved the issue by removing classes from the body and applying them to the #__next container div:

I used the approach in this example for using tailwind with Next.js.

Then I edited styles/index.css

@tailwind base;

/* Write your own custom base styles here */
/* #__next {
  height: 100%;
} */

/* Start purging... */
@tailwind components;
/* Stop purging. */

html,
body {
  @apply bg-gray-50 dark:bg-gray-900;
}

#__next {
  @apply flex flex-col h-screen text-gray-600 leading-normal text-base tracking-normal;
}

/* Write your own custom component styles here */
.btn-blue {
  @apply px-4 py-2 font-bold text-white bg-blue-500 rounded;
}

/* Start purging... */
@tailwind utilities;
/* Stop purging. */

/* Your own custom utilities */

As you said, the point is to add the classes to #__next instead of the body.

As you can see in the comments, it's important where to add the @apply instruction.

The important lines are:

#__next {
  @apply flex flex-col h-screen text-gray-600 leading-normal text-base tracking-normal;
}

As you've asked in your question title, this is one way to add tailwind styles to the #__next container div.

The other solution is to set the classes after the component or page is loaded using the componentDidMount() lifecycle hook or the useEffect hook like this:

useEffect(() => {
    document.querySelector("#__next").className =
      "flex flex-col h-screen text-gray-600 leading-normal text-base tracking-normal";
  }, []);

If you take a look at the Next.js documentation about Custom document you can see that the Main component and NextScript are the internals of Next.js and are required for the page to be properly rendered, and you can not change Next.js internals, so I think the solutions mentioned above are the best ways to add classes to the #__next container div.

Abbas Hosseini
  • 1,545
  • 8
  • 21
  • I think this may be the closest thing to actually solving the problem but not exactly what I was looking for. I'm new to Nextjs and it seems like it arbitrarily adds the #__next div to mount the Nextjs to and it doesn't seem configurable at all. I was hoping to get some insight into the inner workings of Nextjs or possibly a possible improvement that could be made. – Dillan Wilding Dec 28 '20 at 02:32
  • As you've mentioned n your question title: " or add classes to __next div? " these are the ways you can achieve your goal. – Abbas Hosseini Dec 28 '20 at 10:15
  • While it doesn't settle my creative/intellectual query about Nextjs, this solved my problem so I'm going to give you the reward. – Dillan Wilding Dec 30 '20 at 14:25
  • I'm glad I could help! – Abbas Hosseini Dec 30 '20 at 14:46
2

There is no need to inject elements between or replace NextJS' technical root element (#__next). Just stretch all parent elements of your app's root element to take all the screen height with 100vh (vh is "vertical height", a unit which is 1% of the viewport height).

html, body, #__next, #app {
  min-height: 100vh;
}

Your HTML might look like this:

<html>
<head><!-- head content --></head>
<body>
  <div id="__next">
    <div id="app"><!-- this is your app's top element --></div>
  </div>
</body>
</html>
Paul Rumkin
  • 6,737
  • 2
  • 25
  • 35
1

you can change your html or body tags by creating a custom document in ./pages/_document.js

import Document, { Html, Head, Main, NextScript } from 'next/document'

class CustomDocument extends Document {
  static async getInitialProps(ctx) {
    const initialProps = await Document.getInitialProps(ctx)
    return { ...initialProps }
  }

  render() {
    return (
      <Html>
        <Head />
        <body className="custom-class-name">
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

export default CustomDocument

Take into account that these tags DO need to be included in your file: <Html>, <Head />, <Main /> and <NextScript />.

Here's the full documentation

In your case, that element with the __next id is just what the element renders, and it's required for the main app to be mounted on. So you can't remove it or edit it. I think you can add some css in _document.js that targets #__next, using the @apply directive from tailwind might be the way to go instead of editing the HTML of the element (since it doesn't seem to be possible)

Roddy of the Frozen Peas
  • 14,380
  • 9
  • 49
  • 99
Jair Reina
  • 2,606
  • 24
  • 19
  • I have a custom document like outlined here and in the documentation but Nextjs still inserts a div with the id __next between body and the page content. – Dillan Wilding Dec 27 '20 at 04:08
  • I see. That element with the __next id is just what the
    element renders, and it's required for the main app to be mounted on. So you can't remove it or edit it. I think you can add some css in _document.js that targets #__next, using the @apply directive from tailwind might be the way to go instead of editing the HTML of the
    element (since it doesn't seem to be possible)
    – Jair Reina Dec 27 '20 at 22:11
  • That's unfortunate. I almost think that it'd be better to either have the mount point configurable or mount to body rather than an arbitrary div that is inserted. – Dillan Wilding Dec 28 '20 at 02:31
0

I found updating the styles/globals.css to be the easiest way to achieve this.

/*
  Extend the top most enclosing elements to entire height of the screen 
  to allow for the background image to fill the entire screen
*/
html,
body,
#__next {
  height: 100%;
}

Note that that global.css has to be imported into _app.tsx

Archmede
  • 1,592
  • 2
  • 20
  • 37