Change Gatsby Link Behaviour - Stop Scroll Animation on Page Change - reactjs

I'm sure this is going to be a very straight forward answer, but I can't for the life of me work out how to get my Gatsby/React site to start at the top of a new page without a scroll transition effect when I click on an internal link.
Example:
On my home page I scroll to the bottom, then click on a link to take me to an 'About Me' page. The About Me Page loads with the browser scroll position still at the bottom of the page and then the window scrolls to the top of the page. This only takes a few milliseconds but when I click on a link I want to start at the top of the page without any transition.
The links are all standard Gatsby Links:
<Link className="" to="/aboutme/">
About Me
</Link>
Thanks!
EDIT
Adding in my layout wrapper, with useScrollRestoration hooks added:
import React from 'react'
import PropTypes from 'prop-types'
import useScrollRestoration from 'gatsby'
import NavBar from './NavBar/NavBar'
import Footer from './Footer/Footer'
import './layout.scss'
const Layout = ({ children }) => {
const aboutMeScrollRestoration = useScrollRestoration(`page-component-main`)
return (
<>
<NavBar />
<main className="bumpdown" {...aboutMeScrollRestoration}>
{children}
</main>
<Footer />
</>
)
}
Layout.propTypes = {
children: PropTypes.node.isRequired,
}
export default Layout

I managed to work this out in the end after a lot of Googling. It appears not to be a scrollRestoration issue but a Bootstrap issue. In bootstrap/scss/_reboot.scss there's the following code:
:root {
font-size: $font-size-root;
#if $enable-smooth-scroll {
#media (prefers-reduced-motion: no-preference) {
scroll-behavior: smooth;
}
}
}
This can be turned off either using $enable-reduced-motion: true; or $enable-smooth-scroll: false; in bootstrap overrides which stops the scrolling behaviour when a new page opens.
I'm using the enable-smooth-scrolling:false option as the other option may have further knock on effects.

I created a custom.scss file and imported it into the gatsby-browser.js file. The contents of the scss file is overriding the default value of the scroll-behaviour in bootstrap:
$enable-smooth-scroll: false;
:root {
scroll-behavior: auto !important;
}
#import "node_modules/bootstrap/scss/bootstrap";

This issue is known as Scroll Restoration. #reach/router (from where Gatsby extends its routing) handles it automatically. However, in some cases, it fails, especially when rendering containers that have their own scroll values. To bypass this "issue", Gatsby exposes a useScrollRestoration hook to restore the scroll position. For example:
import { useScrollRestoration } from "gatsby"
import countryList from "../utils/country-list"
export default function PageComponent() {
const ulScrollRestoration = useScrollRestoration(`page-component-ul-list`)
return (
<ul style={{ height: 200, overflow: `auto` }} {...ulScrollRestoration}>
{countryList.map(country => (
<li>{country}</li>
))}
</ul>
)
}
So, in your destination component (/aboutme/ page), use the useScrollRestoration hook in your outer wrapper to restore the scroll position.

Related

NextJs Hash in anchor tags

I have an regarding anchor tag in nextjs. The entire section should to land at top, but it lands at the middle portion of the screen specifically in all IOS devices both mobile and tablet.
scene 1
scene 2
JSX
import React from 'react'
export const LinkOne = () => {
return (
Link Text
)
}
CSS
html{
scroll-behavior: smooth;
scroll-padding-top: 50px;
}
I even use this library react-scroll still doesn't work
import React from 'react'
export const LinkOne = () => {
return (
<Link className="test6" to="sectStorage" spy={true} smooth={true} duration={500}>Link Text</Link>
)
}
I doubt the issue might be I wasn't using nextjs link a built in nextjs library, but I will try using it hoping it works. I'd like to hear your ideas and possible solutions in this issue specifically in IOS. Thank you in advance

Get the Gatsby Link destination and conditionally render exit animation with Framer Motion

I've built a small website to learn more about page transition with Gatsby and Framer Motion and Styled Components.
[SPOILER]: My problem to be solved is at the end of the code blocks
The way it's currently working is simple:
An homepage with a list of projects
export default function Home() {
return (
<Layout>
<Welcome />
<WorkList />
<Footer />
</Layout>
)
}
A project page template that generate each project thanks to createPages (here is a simplified version)
import React, { useState, useRef, useContext, useEffect } from "react"
import { Link } from "gatsby"
// Components
...
// Data
import Projects from "../data/works.json"
// Styles
...
// Variants
...
const Project = ({ pageContext }) => {
const project = Projects.find(({ id }) => id === pageContext.id)
// lots of functions here
return (
<Layout>
<ProjectWrapper>
<Container>
<ProjectContent>
<BackgroundLines />
<ProjectContentInner>
<ProjectHeader>
<!-- here the header logic -->
</ProjectHeader>
<ProjectBlocks>
<!-- here the content logic -->
</ProjectBlocks>
</ProjectContentInner>
<ProjectFooter>
<!-- here the footer logic -->
</ProjectFooter>
</ProjectContent>
</Container>
</ProjectWrapper>
</Layout>
)
}
export default Project
The Layout component is holding the navigation
// Components
import Header from "./header"
// Styles
import { GlobalStyle } from "../styles/globalStyles"
const Layout = ({ children }) => {
return (
<div className="app">
<GlobalStyle />
<Header />
<main>{children}</main>
</div>
)
}
export default Layout
and last but not least, the gatsby.browser.js wrapped with the AnimatePresence and the Context Provider
import React from "react"
import { AnimatePresence } from "framer-motion"
import { LocationProvider } from "./src/context/locationContext"
export const wrapPageElement = ({ element }) => (
<LocationProvider>
<AnimatePresence exitBeforeEnter>{element}</AnimatePresence>
</LocationProvider>
)
export const shouldUpdateScroll = () => {
return false
}
So what I want to do seemed easy but it turned out that is not (at least for me).
I've currently made a beautiful transition between 2 projects, similar to the one you could see in here.
If you scroll to the bottom of the page, you can see that the next project's header is shown as a preview and once you click on it, it will smoothly transition to the next project page.
Awesome.
BUT, but this transition is a problem when the user clicks on the link in the navigation that takes him to the home or to another page.
I don't want to have the same exit transition, where some elements disappear while others overlaps, and I don't want the same timing. I want to do something completely different, based on where I'm headed to.
What I thought of as a solution, is to conditionally render exit transition in framer motion, to have different exit animation based on some variables.
I want to be able to track the Link Destination before the component unmount in order to be able to conditionally render an exit transion in Framer Motion
Since, as you may have seen, the navigation isn't inside the project.js I tried with createContext and useContext, getting the location.pathname to have an origin state and a e.target.pathname on Link to have a destination state. This doesn't actually works because everything seems to get a rerender.
I just provided the pieces of codes that seemed crucial to understand the overall structure, but I can go deeper with the way I've built variants or the current exit animations.
I'm not sure if it will help but you can get the props as any React component in the wrapPageElement:
export const wrapPageElement = ({ element, props }) => {
console.log("props", props)
if(props.location.pathname==='/'){
//return some other animation stuff
}
return <LocationProvider {...props}>
<AnimatePresence exitBeforeEnter>{element}</AnimatePresence>
</LocationProvider>
}

how change background color in different pages

i am two page in reactjs
pageOne.js:
import React from "react";
import { Link } from "react-router-dom";
import "./pageOne.css";
const PageOne = () => {
return (
<div>
one
<br />
<Link to="/pageTwo">Two Page</Link>
</div>
);
};
export default PageOne;
pageTwo.js:
import React from "react";
import { Link } from "react-router-dom";
import "./pageTwo.css";
const PageTwo = () => {
return (
<div>
two
<br />
<Link to="/">One Page</Link>
</div>
);
};
export default PageTwo;
i am define two css files for change background color when page loaded.
pageOne.css
body {
background-color: whitesmoke !important;
}
pageTwo.css
body {
background-color: crimson !important;
}
it's problem.in pageOne background color is crimson and in pageTwo background color is crimson.
sample
As I said earlier, there is only one body tag in the DOM tree by default. So when you try to style it whatever comes last will override the previous ones and in your case, the page two style will override the page one style.
To solve this, you got several options, but I will go with the easiest one. You can make a container for each of your pages and then assign a colour to that container to make the whole page background as you desired (You can simply make a layout component then wrap each of the components within it and with similar approach make it reusable). So, for example, you can create your first page like this:
<div className="crimson">
two
<br />
<Link to="/">one Page</Link>
</div>
and style it like this:
.crimson {
background-color: crimson;
min-height: 100vh; /* minimum height of page would be equal to available view-port height */
}
This goes the same for your other page. But you need to consider you have to remove the default margins from the body itself to prevent any disorder.
Working Demo:
I would solve this with Layout component:
const Layout = ({ backgroundColor = '#fff', children }) => (
<div style={{ backgroundColor }} className="layout">
{children}
</div>
)
then remove your css(and try not to use important in your css)
<Layout backgroundColor="#fff"><PageOne /></Layout>
and
<Layout backgroundColor="#f00"><PageTwo /></Layout>

React JS - Material UI: Icons are not visible

I am using Material UI and my goal is to make an Icon button. Therefore I tried this:
import React, { Component } from 'react';
import DoneOutlineSharpIcon from '#material-ui/icons/DoneOutlineSharp';
import IconButton from '#material-ui/core/IconButton'
export class App extends Component {
render() {
return (
<div>
<IconButton> <DoneOutlineSharpIcon style={{color: "green"}}/> </IconButton>
</div>
)
}
}
Additionally, I added the material icons to the index.html like this:
<link href="https://fonts.googleapis.com/icon?family=Material+Icon‌​s" rel="stylesheet">
The Icon is not visible, but pressing F12 in the "Elements" it shows that there is a button.
I am still new to React, so I might have done something stupidly wrong. Can someone tell me what I missed here? And how can I simply place an Icon into a Button?

How do I change the background color of the body?

I'm using React.js and want to change the background color of the entire page. I can't figure out how to do this. Please help, thank you.
Edit (Sep 2 '18): I have a project on GitHub that I'm linking here. I don't have this project online right now, but in the /client folder is the client server. Check it out. This is how I answered the question.
The simplest solution is a bit hacky, but you can use raw javascript to modify the body style:
document.body.style = 'background: red;';
// Or with CSS
document.body.classList.add('background-red');
A cleaner solution could be to use a head manager like react-helmet or next.js Head component.
import React from 'react';
import {Helmet} from 'react-helmet';
class Application extends React.Component {
render () {
return (
<div className="application">
<Helmet>
<style>{'body { background-color: red; }'}</style>
</Helmet>
...
</div>
);
}
};
Some css-in-js also offers tools to manage global level styles; like styled-components injectGlobal.
In the end, there's a lot of tools providing cleaner ways to handle this. But if you don't want to rely on third party, the raw JS option might be good enough if you don't make it too interactive.
The simplest solution without doing anything fancy is to:
1) Open public/index.html
2) Add an inline style to your body like this:
<body style="background-color: red">
try this to your code
componentDidMount() {
document.body.style.backgroundColor = "red"
}
React Helmet (https://github.com/nfl/react-helmet)
I really found this library very helpfull. Really clean solution i would say.
Sample Usage:
import Helmet from 'react-helmet';
<Helmet bodyAttributes={{style: 'background-color : #fff'}}/>
The above solutions tell about adding the external library to give the required functionality but instead what you can do is just go to your Index.css file and inside the already written 'body' tag put "background-color: 'color'" and its done
The basic idea is to use the browser API in raw JavaScript without installing any packages. I don't recommend to use Helmet because it is unsafe to the life cycle of a class component.
// Update the document body background using the browser API
// in render() function for class component or directly in functional component
document.body.style.background = 'red';
Other application:
If you are constantly changing the body's background color, and you want to have the code to run on every render, following is an example. Be careful where you call the browser API.
For a class component, update it in componentDidUpdate().
import React from 'react';
class Example extends React.Component {
// ...
componentDidUpdate() {
// change background color with a random color
const color = Math.floor(Math.random()*16777215).toString(16);
document.body.style.background = color;
}
// ...
}
For a functional component, using Effect Hook useEffect(). You can look more into React Hook here.
import { useEffect } from 'react';
function Example() {
// ...
// Similar to componentDidMount and componentDidUpdate in class component:
useEffect(() => {
// change background color with a random color
const color = Math.floor(Math.random()*16777215).toString(16);
document.body.style.background = color;
});
// ...
}
If you want to change background color before the page loads you can do something like this.
import React, { useLayoutEffect } from 'react'
useLayoutEffect(() => {
document.body.style.backgroundColor = "red"
});
For further reference on useLayoutEffect check here
I just fiddled around with this for a bit. Simple solution for me, not entirely intuitive, all in public/index.html:
<html style="background-color: red">
that gets the bottom of the page, and a second entry:
<body style="background-color: red">
that gets you the top of the page ie. your react code.
Two entries for one problem but it's all in one file, needs no new libs and seems to work.
Please Don't need to install any packages is very easy and simple !
add
document.body.style = 'background: red;';
to your JS code or via CSS like below:
const Changebackground = () => {
const stylesObj = {
background: "green"
};
return ( <
div id = "hello-world"
style = {
stylesObj
}
className = "container" >
<
/div>
)
}
export default Changebackground;
//ReactDOM.render( < Changebackground / > , document.getElementById('App'));
.container {
position: fixed;
width: 100%;
height: 100%;
z-index: -10;
text-align: center;
transition: all 500ms ease;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.5.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.5.1/umd/react-dom.production.min.js"></script>
please check this one to see how its easy !
https://codepen.io/farbod-aprin/pen/dadJyb
After some time scouring the internet, I found this solution on a now deprecated github repo:
document.body.style.backgroundColor = "INSERT_COLOR";
You can post it outside of your function (That's how I used it)
It worked for me in my project: https://github.com/antdke/styled-input-bar/blob/master/src/App.tsx
I needed it to color the background for the entire app.
Where I found the code snippet (At top of the README.md): https://github.com/happylinks/react-body-backgroundcolor
Hope it helps you all out like it helped me :)
You should have a root component (e.g., a div) that is affected by the color you want as the background. That's the react-way to think about it.
<div className ="bg-primary"> // this should cover everything in the background
<div className="container mx-auto bg-secondary"> // this could be a centered container that has a different color.
<MyReactComponent />
</div>
</div>
The style property is not writable in Typescript but setAttribute works okay. Just pop it in your App component. It may not be a good idea to have a random bit of styling outside of React's many other styling mechanisms, it might make it hard to find, but if it's what you need it's what you need. It's what I needed today anyway!
document.body.setAttribute('style', 'background: red;')
You can use a CSS module in React. Below is the Javascript code for a file named Home.js
import styles from './Home.module.css';
const Home = ()=>{
return(
<div className = {styles}>
</div>
);
}
export default Home;
And in a css module named Home.module.css (format is [name].module.css), put the following and your page will have a background colour of blue
body{
background-color: blue;
}
I faced this issue too and this is my solution.
body{
margin:0;
background-color:blue;
}
This was simplest solution for me:
// in your css file
.background-white {
background-color: white !important;
}
// in your react component
componentDidMount() {
document.body.classList.add("background-white");
}
componentWillUnmount() {
document.body.classList.remove("background-white");
}
Using componentWillMount did not work reliably. Using document.body.style did not work reliably either.
If you are using React with Bootstrap, just include a background-color Bootstrap class to the <body> tag, in the index.html file, like this:
<body class="bg-light">
You can choose other colors from https://getbootstrap.com/docs/4.3/utilities/colors/#background-color
In your react src folder you should have a custom.css file.
Add 'body {background-color: ; }' in that custom.css file.
By default, assuming you've used npx Create-React-app MyAppName your React app will have an index.css.
This contains css which controls the default grey background. Just edit this to what you require.
body {
margin: 0;
background-color: #3f3f3f;
}
create index.css file like this
body {
margin: 0;
background-color: rgba(0, 0, 0, 0.07);
}
Then import it in your index.ts file
import "./index.css";
Also keep in mind the background color property might get overwritten if you import other css files below
So your index.ts file look like this
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import "bootstrap/dist/css/bootstrap.css";
import "./index.css";
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
This works for me as long as my body's data has content in it
body { background-color : rgba(235,232,232,0.767); }
Similarly, you can alter the root tag as well.
#root{ background-color: rgba(235, 232, 232, 0.767); }
Make a wrapper componenet with an id like "wrapper" and then create a style with the color state:
getInitialState: function () {
return { color: "white" }; // Set your color for initial state.
},
changeColor: function () {
this.setState({ color: "black" }); // Set your changed color.
},
render: function () {
var style = { backgroundColor: this.state.color };
// The wrapper (root) component
return (
<div id="fullscreen" style={style}>
<a onClick={this.changeColor}>change</a>
</div>
);
}
The result should be something like:
<body>
<div id="fullscreen" style="background-color: YOUR CUSTOM COLOR">
<a onclick="THE REACT FUNCTION"></a>
</div>
</body>
It's been a while since I asked this question. What I've started doing since then is using Facebook's create-react-app setup, and editing CSS files directly.

Resources