Deathspike
Deathspike

Reputation: 8770

Content beneath fixed AppBar

Probably a basic question, but I couldn't find any example in the documentation. Using material-ui-next beta.30. I have the following:

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import * as mui from 'material-ui';
import 'typeface-roboto';

function App() {
  return (
    <div>
      <mui.Reboot />
      <mui.AppBar color="primary" position="fixed">
        <mui.Toolbar>
          <mui.Typography color="inherit" type="title">
            My Title
          </mui.Typography>
        </mui.Toolbar>
      </mui.AppBar>
      <mui.Paper>
        My Content
      </mui.Paper>
    </div>
  );
}

ReactDOM.render(
  <App />,
  document.getElementById('container')
);

And I want the mui.Paper content appear beneath the AppBar, and not hidden by it. Is there a component I'm missing somewhere?

Upvotes: 47

Views: 56531

Answers (6)

wassfila
wassfila

Reputation: 1871

This question is about "content" beneath the fixed appbar. If by content is meant only the very first top of the page, then all answers above are valid, but if by content is also meant links anchors and scrolling to elements ids, headings,... which is pretty much common in most pages, then neither of the provided solution is satisfactory. CSS do have a good fix for that which is scroll-padding-top:

html {
    scroll-padding-top: 80px;
}

https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-padding-top

Upvotes: 1

Petr K.
Petr K.

Reputation: 660

Simply add an empty Toolbar after AppBar:

<AppBar>
  <Toolbar>
    ...toolbar content...
  </Toolbar>
</AppBar>

<Toolbar /> // <- does the trick

Source: https://github.com/mui-org/material-ui/issues/16844#issuecomment-517205129

Upvotes: 32

T L Long
T L Long

Reputation: 45

None of these solutions (completely) worked for me...

The size of my AppBar changes on window resize to-and-from mobile(960px). The theme.mixins.toolbar solution returns a static theme height, which left extra space below my AppBar. The position="sticky" solution puts the AppBar inline, making it inaccessible if you are not at the top of the page.

Here is my solution:

export default function () {
  const nav = useRef();
  const [navHeight, setNavHeight] = useState(0);
  const [isMobile, setMobile] = useState(false);

  useLayoutEffect(() => {
    nav.current && setNavHeight(nav.current.clientHeight);
  }, [isMobile]);

  const checkIsMobile = () => (window.innerWidth < 960 ? true : false);
  if (typeof window !== 'undefined') {
    window.onresize = () => isMobile !== checkIsMobile && setMobile(checkIsMobile);
  } 

  return (
    <>
      <AppBar ref={nav}></AppBar>
      <div style={{ paddingTop: `${navHeight}px`}} />
    </>
  )
}

The useLayoutEffect hook fires when the window.innerWidth crosses the threshold of 960px, triggering setNavHeight, which is used to set the paddingTop on the dummy <div/> below the <AppBar/>.

This solution works with Material-UI 4.11.0 and Next.js 9.4.4 as of 2020/06/31.

Upvotes: 2

Eugene Pakhomov
Eugene Pakhomov

Reputation: 10652

The Elevate App Bar example just adds an empty Toolbar:

export default function ElevateAppBar(props) {
  return (
    <React.Fragment>
      <CssBaseline />
      <ElevationScroll {...props}>
        <AppBar>
          <Toolbar>
            <Typography variant="h6">Scroll to Elevate App Bar</Typography>
          </Toolbar>
        </AppBar>
      </ElevationScroll>
      <Toolbar />  // <-- The important part.
      <Container>
        <Box my={2}>
          {[...new Array(12)]
            .map(
              () => `Cras mattis consectetur purus sit amet fermentum.
Cras justo odio, dapibus ac facilisis in, egestas eget quam.
Morbi leo risus, porta ac consectetur ac, vestibulum at eros.
Praesent commodo cursus magna, vel scelerisque nisl consectetur et.`,
            )
            .join('\n')}
        </Box>
      </Container>
    </React.Fragment>
  );
}

Upvotes: 12

andy
andy

Reputation: 1882

Just use position="sticky" instead of position="fixed" for your AppBar.

Upvotes: 86

Jules Dupont
Jules Dupont

Reputation: 7567

Your content is on screen, but covered up by the AppBar. You can use theme.mixins.toolbar to load information about the app bar height and shift your content accordingly:

const styles = theme => ({
  // Load app bar information from the theme
  toolbar: theme.mixins.toolbar,
});

And then create a div above your content to shift your content accordingly:

<Paper>
  <div className={classes.toolbar} />
    MyContent will be shifted downwards by the div above. If you remove 
    the div, your content will disappear under the app bar.
</Paper>

What's happening here is that theme.mixins.toolbar is loading information about the AppBar dimensions into your styles. Then, applying that information to the div sizes the div so that it's exactly the right height for shifting your content down the screen.

Here's a full working example:

import React from 'react';
import Paper from 'material-ui/Paper';
import Reboot from 'material-ui/Reboot';
import AppBar from 'material-ui/AppBar';
import Toolbar from 'material-ui/Toolbar';
import Typography from 'material-ui/Typography';
import { withStyles } from 'material-ui/styles';

const styles = (theme) => ({
  toolbar: theme.mixins.toolbar,
});

const App = (props) => {
  const { classes } = props;

  return (
    <div>
      <Reboot />
      <AppBar color="primary" position="fixed">
        <Toolbar>
          <Typography color="inherit" type="title">
            My Title
          </Typography>
        </Toolbar>
      </AppBar>
      <Paper>
        <div className={classes.toolbar} />
        MyContent will be shifted downwards by the div above. If you remove 
        the div, your content will disappear under the app bar.
      </Paper>
    </div>
  );
}

export default withStyles(styles)(App);

Upvotes: 34

Related Questions