Ways to implement a fixed navigation bar beneath the primary navbar using ReactJS

Utilizing ReactJS, I am endeavoring to create a secondary (smaller) navbar in the same style as Airtable's product page. My primary navbar is situated at the top and transitions from transparent to dark when scrolled. The secondary bar (highlighted in purple for visibility in the screenshot) currently resides behind the primary navbar, but my aim is for it to be positioned right below the header background image.

Main Navbar - "Header.js"

import React, { useEffect, useState } from 'react';
import { makeStyles } from '@material-ui/core/styles';
import { AppBar, IconButton, Toolbar, Collapse } from '@material-ui/core';
import SortIcon from '@material-ui/icons/Sort';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import { Link as Scroll } from 'react-scroll';
import ScrollToColor from './ColorChangeOnScroll';

const useStyles = makeStyles((theme) => ({
    root: {
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
        height: '100vh',
        fontFamily: 'Nunito',
    },
    appbar: {
        background: 'none',
    },
    appbarTitle:{
        flexGrow: '1',
    },
    appbarWrapper:{
        width: '80%',
        margin: '0 auto',
    },
    icon: {
        color: '#fff',
        fontSize: '2rem',
    },
    colorText: {
        color: '#34cbc2',
    },
    container: {
        textAlign: 'center',
    },
    title: {
        color: '#fff',
        fontSize: '4rem',
    },
    goDown: {
        color: '#34cbc2',
        fontSize: '4rem',
    },
}));

export default function Header() {
    const classes = useStyles();
    const [checked, setChecked] = useState(false);
    useEffect(() => {
        setChecked(true);
    }, []);

    return (
        <section>
        <div className={classes.root} id="header">
            <ScrollToColor>
            <AppBar className={classes.appbar} elevation={0}>
                <Toolbar className={classes.appbarWrapper}>
                    <h1 className={classes.appbarTitle}>
                        Logo <span className={classes.colorText}>Colored</span>
                        </h1>
                    <IconButton>
                        <SortIcon className={classes.icon} />
                    </IconButton>
                </Toolbar>
            </AppBar>
            </ScrollToColor>

            <Collapse
            in={checked}
            {...(checked ? { timeout: 1000 } : {})}
            collapsedHeight={50}>
                <div className={classes.container}>
                    <h1 className={classes.title}>
                        Main header <br />
                        at <span className={classes.colorText}>title.</span>
                    </h1>
                    <Scroll to="info-card" smooth={true}>
                    <IconButton>
                      <ExpandMoreIcon className={classes.goDown} />
                    </IconButton>
                    </Scroll>
                </div>
            </Collapse>
        </div>
        </section>
    );
}

Secondary Navbar - "StickyHeader.js"

import React, { useEffect, useState } from 'react';
import { makeStyles } from '@material-ui/core/styles';
import { AppBar, IconButton, Toolbar, Collapse } from '@material-ui/core';
import SortIcon from '@material-ui/icons/Sort';

const useStyles = makeStyles((theme) => ({
    root: {
        top: '1000px',
        display: 'flex',
        position: 'sticky',
        justifyContent: 'center',
        alignItems: 'center',
        fontFamily: 'Nunito',
    },
    appbar: {
        background: 'purple',
    },
    list: {
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
    },
}));

export default function Header() {
    const classes = useStyles();
    const [checked, setChecked] = useState(false);
    useEffect(() => {
        setChecked(true);
    }, []);

    return (
        <section>
        <div className={classes.root} style={{ top: "72px" }} id="stickyheader">
            <AppBar className={classes.appbar} elevation={4}>
                <Toolbar className={classes.appbarWrapper}>
                    <ul className={classes.list} style={{ listStyleType: "none" }}>
                        <li>
                            <a href="#product" data-id="product" data-is-active="true">Database</a>
                        </li>
                    </ul>
                </Toolbar>
            </AppBar>
        </div>
        </section>
    );
}

Landing Page snippet

export default function Landing() {
  const classes = useStyles();

  return (
      <div className={classes.root}>
          <CssBaseline />
          <Header />
          <StickyHeader />
          <InfoCards />

      </div>
  );
}

Tried adjusting:

  • Segmenting each component into their own files to serve as separate 'sections' stacked on top of one another
  • Modifying margin, marginTop properties in useStyles with position: relative
  • Applying a similar approach with position: sticky
  • Including style={{ top: "##px" }} to the navbar's div to test if it shifts downward

Images Secondary navbar behind primary at top https://i.stack.imgur.com/PorBU.jpg

Secondary navbar behind primary when scrolled https://i.stack.imgur.com/LuNmi.jpg

Conceptual view of the desired outcome (secondary navbar sticking and merging with primary navbar upon scrolling) https://i.stack.imgur.com/P9zfs.jpg

I'm unsure if there is a simple styling adjustment needed for these navbars or if a more intricate solution is required. Any guidance provided would be greatly appreciated.

Note The logo and header titles belong to the first navbar. The second (purple) navbar features a small 'Database' clickable text that may not be clearly visible in the initial screenshots due to its placement over 'Logo colored.'

Update Screenshot result from MB_'s answer https://i.stack.imgur.com/QoErl.png

Answer №1

UPDATE

This solution is unique and effective...

Note:

1/ Replace position: "sticky" with position: "fixed"

2/ Additional modifications are required, such as adding a scroll listener.

Header Component

import React, { useEffect, useState } from "react";
import { makeStyles } from "@material-ui/core/styles";
import { AppBar, IconButton, Toolbar, Collapse } from "@material-ui/core";
import SortIcon from "@material-ui/icons/Sort";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import { Link as Scroll } from "react-scroll";

const useStyles = makeStyles(theme => ({
  root: {
    fontFamily: "Nunito"
  },
  appbar: {
    position: "fixed",
    zIndex: "9999",
    background: "black"
  },
  appbarTitle: {
    flexGrow: "1"
  },
  appbarWrapper: {
    width: "80%",
    margin: "0 auto"
  },
  icon: {
    color: "#fff",
    fontSize: "2rem"
  },
  colorText: {
    color: "#34cbc2"
  },
  container: {
    textAlign: "center",
    height: "100vh",
    marginTop: "80px",
    display: "flex",
    flexDirection: "column",
    justifyContent: "center",
    alignItems: "center"
  },
  title: {
    color: "#333",
    fontSize: "4rem"
  },
  goDown: {
    color: "#34cbc2",
    fontSize: "4rem"
  }
}));

export default function Header() {
  const classes = useStyles();
  const [checked, setChecked] = useState(false);
  useEffect(() => {
    setChecked(true);
  }, []);

  return (
    <div className={classes.root} id="header">
      <AppBar className={classes.appbar} elevation={0}>
        <Toolbar className={classes.appbarWrapper}>
          <h1 className={classes.appbarTitle}>
            Logo <span className={classes.colorText}>Colored</span>
          </h1>
          <IconButton>
            <SortIcon className={classes.icon} />
          </IconButton>
        </Toolbar>
      </AppBar>

      <Collapse
        in={checked}
        {...(checked ? { timeout: 1000 } : {})}
        collapsedHeight={50}
      >
        <div id="mainheader" className={classes.container}>
          <h1 className={classes.title}>
            Main header <br />
            at <span className={classes.colorText}>title.</span>
          </h1>
          <Scroll to="info-card" smooth={true}>
            <IconButton>
              <ExpandMoreIcon className={classes.goDown} />
            </IconButton>
          </Scroll>
        </div>
      </Collapse>
    </div>
  );
}

StickyHeader Component

import React, { useEffect, useState } from "react";
import { makeStyles } from "@material-ui/core/styles";
import { AppBar, IconButton, Toolbar, Collapse } from "@material-ui/core";

const useStyles = makeStyles(theme => ({
  root: {
    fontFamily: "Nunito"
  },
  appbar: {
    background: "purple"
  },
  list: {
    display: "flex",
    justifyContent: "center",
    alignItems: "center"
  }
}));

export default function StickyHeader() {
  const classes = useStyles();
  const [checked, setChecked] = useState(false);
  const [scrollY, setScrollY] = useState({ position: "relative" });

  useEffect(() => {
    window.addEventListener("scroll", () => {
      const heightVh = document.querySelector("#mainheader").offsetHeight / 100;

      if (window.scrollY > heightVh * 100) {
        setScrollY({ position: "fixed", top: "80px" });
      } else {
        setScrollY({ position: "relative" });
      }
    });
    return () => {
      window.removeEventListener("scroll");
    };
  }, [scroll]);

  useEffect(() => {
    setChecked(true);
  }, []);

  return (
    <>
      {console.log(scrollY)}
      <div className={classes.root} id="stickyheader">
        <AppBar className={classes.appbar} style={scrollY} elevation={4}>
          <Toolbar className={classes.appbarWrapper}>
            <ul className={classes.list} style={{ listStyleType: "none" }}>
              <li>
                <a href="#product" data-id="product" data-is-active="true">
                  Database
                </a>
              </li>
            </ul>
          </Toolbar>
        </AppBar>
      </div>
    </>
  );
}

Demo : Code Sample on StackBlitz

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is it necessary to list react-native as a dependency for my react-native library?

As I develop a react-native library that relies on certain react-native modules to function properly, do I need to specify react-native as a dependency in the package.json file? I've noticed that some other react-native npm packages list react-native ...

What is the best way to pass a reference from one component to another component using a mapped list?

Here is an array of objects representing services: export const serviceList = [ { title: "Exterior Services", image: "/images/service-card-images/kendall-painting.webp", description: "ExteriorServicesDescription" ...

What is the best method to automatically check all checkboxes when the dropdown menu is not in use?

Currently, I am utilizing the Material UI multiple select feature with the intention of having all checkboxes automatically checked, including the "All" option, if no selections are made before closing the dropdown menu. My approach involves using a bool ...

A guide on retrieving the value of input elements using class names in jQuery

I need help fetching the values of input elements with the class "features". I am able to access all of them. alert($('.features').length); Can someone please confirm if my understanding is correct? $('.features') is an array of HTM ...

What is the method by which react-native-measure-text determines height?

Is there a way to determine the height of a text element in react-native without actually rendering it on the screen? I've heard about using react-native-measure-text for this purpose, but I'm curious about how exactly this library calculates th ...

Personalize Your MUI Drawer Backdrop

I'm looking to customize the backdrop color of an MUI drawer. After attempting to change the color using styled components, I encountered an issue with setting the opacity: const DrawerStyle = styled(Drawer) ({ '& .MuiBackdrop-root' : ...

It appears that using JQuery's .when and .done functions may result in the code executing before the script has finished loading

Since updating the hard-coded <script> with JQuery promises, I have been encountering these errors frequently: https://i.stack.imgur.com/xkWAk.png The issue seems to be inconsistent in replicating. Sometimes, the error occurs while loading the page ...

Experience the quick sort programming algorithm in action using Vue.js without the hassle of dealing with Promises

I am currently experimenting with the quicksort algorithm visualization using Vue.js for self-educational purposes. However, I am facing some difficulties with async/await implementation. Although array filling and shuffling seem to work fine (although I a ...

Issue with Jquery firing function during onunload event

I'm having trouble adding a listener to a form in order to trigger an ajax call when the user leaves it. I can't seem to identify any errors in Firefox and nothing is getting logged in the console, indicating that my code might be incorrect. As s ...

Validating React Typescript Props: Ensuring that two specific props do not exist simultaneously

Currently, I'm developing a reusable component in React-Typescript and I am looking to validate my props OnClick and component as follows: Both onClick and component prop are optional. These props will only be passed to the component if they need to ...

Steps for appending a string to a variable

Currently working on creating a price configurator for a new lighting system within homes using Angular 7. Instead of using TypeScript and sass, I'm coding it in plain JavaScript. Page 1: The user will choose between a new building or an existing one ...

The router.push function does not properly redirect to the specified path; instead, it just reloads the current page

I am a newcomer to NextJS and facing a challenge where I need to transfer data from the current page to another page. However, instead of loading the defined path in router.push as pathname: "/booking/checkout/", it loads the current page. I wan ...

Guide to customizing the layout preview specifically for certain components in Storybook, without affecting all components

Currently working on my storybook project and facing an issue. I'm aiming to have the layout centered in the preview section. I attempted export const parameters = { layout: 'centered', }; in the .storybook/preview.js file However, this c ...

Utilize React Material UI Slider to dynamically adjust Border Radius in your web design

Utilizing React Material UI's components like the slider and button is essential for this project. The main objective is to dynamically change the border radius of the button using the value obtained from the slider. However, there seems to be a chall ...

Is there a way to restrict the amount of RAM Nextjs uses during development?

I am currently working on a project using Nexjs with @mui/material. There is an ongoing issue regarding memory usage in Nextjs, which can be found on this GitHub link. Whenever I run the development server for a period of time, my laptop's RAM gets ...

What causes the discrepancy in CSS behavior between local and remote websites?

My chrome extension enhances facebook chatbox with jquery autocompletion. I am trying to make the suggestion list menu horizontal by modifying the jquery-ui.css. When changing display:block to display:inline, the list becomes horizontal in a local HTML fil ...

Transitioning from utilizing Jquery for post requests to implementing modern Promises

Currently, I am involved in a web application project that utilizes Flask and Python on the back-end with JavaScript on the front-end. I have been contemplating leveraging more modern styles like ES6/7 features such as Promises. In my development process, ...

What could be the reason for the malfunction of the min value in my Material UI date textfield?

I am attempting to set a minimum date for the picker to start from, but it does not seem to be working as expected. <TextField id="date" type="date" defaultValue="2017-05-24" minDate="24/01/2019" ...

Showing post response (XMLHttpRequest) on Chrome extension interface instead of Python console

I am currently developing a Chrome extension that sends a post request with information about the specific URL being browsed by the user to Flask (local host). A web scraping process is then carried out on this URL to determine a category based on the obta ...

Is the accuracy of the in-situ convolution filter guaranteed?

After analyzing my simple box blur function, I have come to the realization that it may be incorrect. The current implementation edits the ImageData object in place, leading to convolution filters depending on surrounding pixels that have already been ch ...