How to make a sticky footer in react? - css

I've made a sticky footer higher-level component that wraps other components inside itself:
Footer.js
//this is a higher-order component that wraps other components placing them in footer
var style = {
backgroundColor: "#F8F8F8",
borderTop: "1px solid #E7E7E7",
textAlign: "center",
padding: "20px",
position: "fixed",
left: "0",
bottom: "0",
height: "60px",
width: "100%",
};
const Footer = React.createClass({
render: function() {
return (
<div style={style}>
{this.props.children}
</div>
);
}
});
export default Footer;
Usage:
<Footer><Button>test</Button></Footer>
But it is hiding the contents of the page:
This looks like a common problem, so I searched a bit and found this issue, where is FlexBox is recommended for the sticky footer. But at this demo the footer is at the very bottom of the page, while I need the footer to be always displayed on the page and the content being scrolled inside the above area (like in SO chat). In addition to that, there is an advice to change all the other components with custom stylesheet rules. Is it possible to achieve what I need using styling only the footer component so the code will remain modular?

Here's an idea (sandbox example link).
Include a phantom div in your footer component that represents the footer's position that other dom elements will respect (i.e. affecting page flow by not being position: 'fixed';).
var style = {
backgroundColor: "#F8F8F8",
borderTop: "1px solid #E7E7E7",
textAlign: "center",
padding: "20px",
position: "fixed",
left: "0",
bottom: "0",
height: "60px",
width: "100%",
}
var phantom = {
display: 'block',
padding: '20px',
height: '60px',
width: '100%',
}
function Footer({ children }) {
return (
<div>
<div style={phantom} />
<div style={style}>
{ children }
</div>
</div>
)
}
export default Footer

Much easier idea (following the trend), i imported both bootstrap and reactstrap, used the bootstrap fixed bottom class and workaround with that like this.
class AppFooter extends Component{
render() {
return(
<div className="fixed-bottom">
<Navbar color="dark" dark>
<Container>
<NavbarBrand>Footer</NavbarBrand>
</Container>
</Navbar>
</div>
)
}

There is a much simpler way. I am creating a portfolio site with React, and some of my pages are not very long, so in some devices, like kindle fire hd for example, the footer would not stick to the bottom. And of course to set this up in the traditional fashion with would not work, because the would be wrapped in there. And we don't want that. So this is what I did:
In App.js:
import React, { Component } from 'react';
import {Header} from './components/Header';
import {Main} from './components/Main';
import {Footer} from './components/Footer';
class App extends Component {
render() {
return (
<div className="App Site">
<div className="Site-content">
<div className="App-header">
<Header />
</div>
<div className="main">
<Main />
</div>
</div>
<Footer />
</div>
);
}
}
export default App;
And then in _sticky-footer.css (I use POSTCSS):
:root {
--space: 1.5em 0;
--space: 2em 0;
}
.Site {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.Site-content {
flex: 1 0 auto;
padding: var(--space) var(--space) 0;
width: 100%;
}
.Site-content:after {
content: '\00a0';
display: block;
margin-top: var(--space);
height: 0;
visibility: hidden;
}
The original solution for this was created by Philip Walton: https://philipwalton.github.io/solved-by-flexbox/demos/sticky-footer/

You can fix this by adding margin-bottom: 60px; to the body of your website. With the 60px being the height of your footer.

.footer{
width: 100%;
position: fixed;
bottom: 0;
}
This should do the trick! Cheers! (:

.App will be the main component you load to your Root.
Assume that the footer is the last child of .App in the document flow
.App {
height: 100vh;
display: flex;
flex-direction: column;
}
footer {
margin-top: auto;
}

I found that if you wrap your 'footer' component in a standard html
<footer>
tag, it pretty much sorts out all of the positioning for you

I wanted to share this solution that worked. I cribbed this from https://react.semantic-ui.com/modules/sticky. Scroll to the bottom of this page and inspect the text 'This is the bottom' to see where I stole it. Its a site built on react so it should work for your situation.
Here it is:
{
padding-top: 50vh;
}
Conceptually, this solution is creating negative space like jacoballenwood's phantom div to push the footer down to the bottom and stick it there. Just add it to your css style class for the footer and adjust the value to taste.

Very late answer, but someone can find this useful. You can, instead of phantom style, set Toolbar. I have build some standard layout for the components, where {children} is component from the parent component - App.js. This is example:
import React from "react";
import { Route } from "react-router-dom";
import { makeStyles } from "#material-ui/core/styles";
import AppBar from "#material-ui/core/AppBar";
import CssBaseline from "#material-ui/core/CssBaseline";
import Toolbar from "#material-ui/core/Toolbar";
import Header from "../components/header";
import Footer from "../components/footer";
import SideBar from "../components/sidebar";
const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
},
appBar: {
zIndex: theme.zIndex.drawer + 1,
},
content: {
flexGrow: 5,
padding: theme.spacing(3),
},
}));
const StandardLayout = ({ children }) => {
const classes = useStyles();
return (
<div className={classes.root}>
<CssBaseline />
<AppBar position="fixed" className={classes.appBar}>
<Route path="/" component={Header} />
</AppBar>
<SideBar />
<main className={classes.content}>
<Toolbar />
<br />
{children}
<Toolbar/>
</main>
<AppBar className={classes.appBar}>
<Route path="/" component={Footer} />
</AppBar>
</div>
);
};
export default StandardLayout;

Its rule for me
<footer style={{position:"fixed",bottom:"0"}}>

Try this html code:
/public/index.html
<html lang="en" class="h-100">
<body class="h-100">
<div id="root" class="d-flex flex-column h-100"></div>
...
/src/App.js
<main role='main' className='flex-shrink-0'>
You can follow this template:
react-bootstrap-sticky-footer/public/index.html
react-bootstrap-sticky-footer/src/App.js

Related

How to make a footer with React and Material UI

I'm trying to make a footer (non-sticky) that appears on every page of my Next.js app. I'm using Material UI for styling.
This is the component that defines the layout for every page of my app:
import React from "react";
import NavBar from "./NavBar";
import Header from "./Header";
import styles from "../styles/Layout.module.css";
import { Container } from "#mui/material";
import Footer from "./Footer";
export default function Layout(props) {
return (
<>
<NavBar></NavBar>
<Container className={styles.container}>
<main className={styles.main}>
<Header />
{props.children}
<Footer />
</main>
</Container>
</>
);
}
The Container component centers its children on the page. The footer is a different color than the background of the pages, so I want the footer background to fill the entire viewport, but the footer content to remain centered with the rest of the page content
I initially tried to do this by translating the content:
import React from "react";
const footerHeight = 300;
const footerEltMarginTop = 15;
const div1Style = {
width: "100vw",
height: `${footerHeight + footerEltMarginTop}px`,
backgroundColor: "blue",
};
const div2Style = {
transform: `translate(0px, -${footerHeight}px)`,
color: "white",
width: "100%",
height: `${footerHeight}px`,
marginTop: `${footerEltMarginTop}px`,
};
export default function Footer() {
return (
<>
<div style={div1Style}></div>
<div style={div2Style}>
<div>footer content</div>
</div>
</>
);
}
The problem was that this left whitespace of height footerHeight below the footer. I instead tried again by making the positions of the footer background and footer content have absolute positoning:
import React from "react";
const footerHeight = 300;
const footerEltMarginTop = 15;
const div1Style = {
width: "100vw",
height: `${footerHeight + footerEltMarginTop}px`,
backgroundColor: "blue",
marginTop: "30px",
position: "absolute",
};
const div2Style = {
width: "100%",
position: "absolute",
color: "white",
height: `${footerHeight}px`,
marginTop: `${footerEltMarginTop}px`,
};
export default function Footer() {
return (
<div style={{width: "100%"}}>
<div style={div1Style}></div>
<div style={div2Style}>
<div>footer content</div>
</div>
</div>
);
}
The problem with this is that the footer background for some reason has whitespace to the left, and does not take up the full viewport.
Does anyone know how one might go about accomplishing this with React + Material UI? Any advice is greatly appreciated! Thanks!
There could be many approaches depending on the styles of Container and main.
Assuming that the goal is to have Footer fill the width of the viewport, perhaps try add disableGutters and maxWidth={false} to Container, so that its default spacing on both sides are disabled, and can extent to the full width of viewport.
Also consider to add CssBaseline to the main Layout as it contains CSS reset recommended by MUI, but this is optional in the use case.
Live demo of a basic example: stackblitz
import CssBaseline from '#mui/material/CssBaseline';
function Layout(props) {
return (
<>
<CssBaseline />
<NavBar></NavBar>
<Container maxWidth={false} disableGutters>
<main>
<Header />
{props.children}
<Footer />
</main>
</Container>
</>
);
}

How to create a global container in react?

I am just starting to learn React, and I would like to know how do you create a global layout container and a global content container. Here is what I am trying to achieve using CSS. BUT I am confused about how to achieve this with React. Would be great for any advice/links to tutorials etc... thanks.
.global-container {
width: 100vw;
height: 100vh;
}
.global-content {
width: 90%;
height: 100%;
margin: 0 auto;
}
Since, you have tagged styled-components I'm assuming you're using it too!
//GlobalContainer.js
import styled from 'styled-components';
const Wrapper = styled.div`
width: 100vw;
height: 100vh;
`
export default function GlobalContainer({ children }) {
return (
<Wrapper>
{children}
</Wrapper>
);
}
// GlobalContent.js
import styled from 'styled-components';
const Wrapper = styled.div`
width: 90%;
height: 100%;
margin: 0 auto;
`
export default function GlobalContent({ children }) {
return (
<Wrapper>
{children}
</Wrapper>
);
}
In the App.js put a div for your global container and content just as you would in HTML. Note that you have to use className instead of class to give your jsx a css class.
import "./Styles/index.css"
function App() {
return (
<div className="global-container">
<div className="global-content">
{// Your app goes here
}
</div>
</div>
);

Using Prismjs for syntax highlighted code blocks is breaking layout on mobile - <pre> element wont take dynamic width

I'm using Prismjs alongside Mdx for a code-related blog. I'm using it to show code blocks in a manner consistent with other blogs.
I'm running into an issue where the rendered code blocks (inside a <pre> element are too wide on my mobile layout. For now I am content to have things scroll on the horizontal axis. I'm 99% certain that the <pre> elements are what's breaking the layout because when I comment them out of the blog post, the layout works as expected.
Specifically, I'm using a package called prism-react-renderer (alongside Gatsby), and the code I have for the CodeBlock element (that handles the syntax highlighting) is more or less verbatim from the documentation for prism-react-renderer, but is included here for convenience:
import React from 'react'
import Highlight, { defaultProps } from 'prism-react-renderer'
import theme from 'prism-react-renderer/themes/nightOwl'
const CodeBlock = (props) => {
const className = props.children.props.className || ''
const matches = className.match(/language-(?<lang>.*)/)
return (
<Highlight {...defaultProps} code={props.children.props.children.trim()} language={
matches && matches.groups && matches.groups.lang
? matches.groups.lang
: ''
}
theme={theme}>
{({ className, style, tokens, getLineProps, getTokenProps }) => (
<pre className={className} style={{ ...style }}>
<code>
{tokens.map((line, i) => (
<div key={i} {...getLineProps({ line, key: i })}>
{line.map((token, key) => (
<span key={key} {...getTokenProps({ token, key })} />
))}
</div>
))}
</code>
</pre>
)}
</Highlight>
)
}
export default CodeBlock
This is the component used in the blog post template that handles rendering the .mdx files into HTML:
import React from 'react'
import { Link, graphql } from 'gatsby'
import { MDXRenderer } from 'gatsby-plugin-mdx'
import { MDXProvider } from '#mdx-js/react'
import Layout from '../components/layout'
import CodeBlock from '../components/code-block'
const components = {
pre: CodeBlock
}
const BlogPostTemplate = ({ data, pageContext, location }) => {
const post = data.mdx
const { previous, next } = pageContext
return (
<Layout>
*** Removed irrelevant component ***
<MDXProvider components={components}>
<div className='blog-post-wrapper'>
<article className='blog-post-content'>
<header>
<h1>
{post.frontmatter.title}
</h1>
<time dateTime={post.frontmatter.date}>
{post.frontmatter.date}
</time>
</header>
<MDXRenderer>{post.body}</MDXRenderer>
</article>
<footer className='blog-post-footer'>
*** Removed irrelevant components ***
</footer>
</div>
</MDXProvider>
</Layout>
)
}
export default BlogPostTemplate
I have tried a few different things: flex shrink, applying overflow-x: scroll and overflow-x: auto to both the <pre> element and its parents. When I apply a fixed width to the <pre> element and overflow-x: scroll I can get the behavior I want but I'd like to not have to use a fixed width on this if possible. The .css I have looks like this, including some obviously ineffectual styles:
.blog-post-wrapper {
display: flex;
flex-direction: column;
overflow-y: scroll;
overflow-x: scroll;
}
.blog-post-content {
flex-grow: 1;
margin-bottom: 2rem;
width: 100%;
display: flex;
flex-direction: column;
overflow-y: scroll;
overflow-x: scroll;
}
.blog-post-content .prism-code {
padding: 20px;
border: 3px solid red;
flex-shrink: 1;
overflow-y: scroll;
overflow-x: scroll;
}
I'll attach images of the way the <pre> element is rendering presently, in inspector:
And this is how it looks if I set a fixed width (in inspector):
It's probably too late, but I had the same issue and I was able to fix it by
Adding max-width css property to the main layout. The value should be equal to window.screen.width. I had to use the following hack to be able to get the screen size:
const [windowWidth, setWindowWidth] = useState(width)
useEffect(() => {
setWindowWidth(window.screen.width)
}, [])
Adding overflow: scroll to the pre in the CodeBlock
Not ideal, but I found this combination of CSS properties working together:
pre code {
display: inline-block;
width: 80vw;
overflow-x: auto;
}

How can I remove the elevation (box shadow) from a material-ui AppBar over one small section?

I'm trying to get rid of the elevation shadow of the navbar over the sidebar.
I'm using Material-UI's AppBar for my NavBar component.
export default function NavBar() {
return (
<div>
<AppBar position="fixed" elevation={4}>
<Toolbar variant="regular">
<IconButton edge="start" color="inherit" aria-label="menu">
<MenuIcon />
</IconButton>
</Toolbar>
</AppBar>
</div>
);
}
I am using a custom sidebar component,
.sidebar {
left: 0;
top: 64px;
height: 100vh;
width: 70px;
background-color: #3f50b5;
position: fixed;
}
.sidebar::before{
display: none;
}
I don't want to lose the elevation/shadow of the entire NavBar, just the section on the left where it's over the Sidebar.
Adding this because I spent a lot of timing finding a simple answer to removing the elevation.
The simplest way to remove the shadow is by adding elevation prop to the AppBar.
<AppBar position="fixed" elevation={0}>
you could add a ::after pseudo element to your navbar to do the job. This element would have the width of your sidebar, and would be have a top property accordingly to your navbar.
Given the AppBar itself has some different breakpoints you would need to change top accordingly:
const useStyles = makeStyles((theme) => ({
root: {
"&::after": {
position: "absolute",
content: '""',
width: "70px",
height: "8px",
top: "48px",
backgroundColor: theme.palette.primary.main,
[theme.breakpoints.down("xs")]: {
top: "56px"
},
"#media (orientation: landscape)": {
top: "48px"
},
[theme.breakpoints.up("sm")]: {
top: "64px"
}
}
}
}));
export default function NavBar() {
const classes = useStyles();
return (
<div>
<AppBar classes={{ root: classes.root }} position="fixed" elevation={4}>
<Toolbar variant="regular">
<IconButton edge="start" color="inherit" aria-label="menu">
<MenuIcon />
</IconButton>
</Toolbar>
</AppBar>
</div>
);
}
I created a sandbox with only the navbar, and a darker shadow to enhance contrast:

Material UI Paper element not covering 100% of viewport height

I have a Material UI <Paper> component that serves as a background and exists in my main React component- it's nested inside a <ThemeProvider>, which is nested a <div>, which is then nested in the <body>. I've applied the viewport: 100vh attribute to make it take the full height of the screen. It does take up the full height, but only prior to rendering another <Paper> component on the right hand side. Then the bottom of the paper no longer extends to the bottom of the screen:
Beginning of App render method:
return (
<ThemeProvider theme={theme}>
<Paper style={{ height: '100vh', boxShadow: 'none' }}>
<Container fluid id='app'>
.......
)
I tried applying the viewport: 100vh attribute to both the <div> that encloses the App component in index.js and the <body> element in index.html. There wasn't any difference. It may be worth mentioning that I'm using react-bootstrap Containers/Rows/Cols for my grid system at the moment (haven't switched that part to Material UI yet), but they're all nested inside the Paper, so I wouldn't expect they would be causing the problem. I also tried removing any css applied to the <Container> but it didn't help.
I'm also using a muiTheme for the <ThemeProvider> (obviously):
export default function createTheme(isDarkModeEnabled) {
return createMuiTheme({
palette: {
type: isDarkModeEnabled ? 'dark' : 'light',
primary: {
main: '#6DD3CE',
dark: '#639FAB'
},
secondary: {
main: '#52CBC5'
}
},
typography: {
fontFamily: [ 'montserratlight', 'Times New Roman' ].join(','),
body2: {
fontFamily: [ 'montserratmedium', 'Times New Roman' ].join(',')
},
h3: {
fontSize: '1.75rem'
},
button: {
fontFamily: [ 'montserratmedium', 'Times New Roman' ].join(',')
}
}
})
}
Update and Solution
I did redo my layout using flexbox instead of react-bootstrap, and ultimately fixed the problem by using min-height: 100vh instead of height: 100vh for my container so it had room to expand.
It appears that your <Container> has a bit of padding that is causing the content to go beyond its height:
This could also be due to your <textarea /> not having an assigned height. This is a particular issue when you are in a medium-sized screen.
If you are already planning to do away with Bootstrap's layout system, consider flex box and styled containers:
import React from "react";
import styled from "styled-components";
export default function App() {
return (
<Container>
<StyledHeader>Hello StackOverflow</StyledHeader>
<StyledCol>
<p>This is column 1.</p>
</StyledCol>
<StyledCol>
<p>This is another column.</p>
</StyledCol>
</Container>
);
}
const Container = styled.div`
height: 700px;
width: 100%;
display: flex;
flex-flow: row wrap;
justify-content: space-between;
`;
const StyledHeader = styled.h1`
height: 30%;
width: 100%;
margin: 0;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
`;
const StyledCol = styled.div`
height: 70%;
width: 50%;
& p {
text-align: center;
}
`;
CodeSandbox Example: https://codesandbox.io/s/stack-63802170-flexbox-example-909ol
This will ultimately give you more control over the layout of the page.

Resources