Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Top-level app component and animated route transitions #88

Closed
dlindenkreuz opened this issue Oct 26, 2016 · 135 comments · Fixed by #4129
Closed

Top-level app component and animated route transitions #88

dlindenkreuz opened this issue Oct 26, 2016 · 135 comments · Fixed by #4129

Comments

@dlindenkreuz
Copy link
Contributor

dlindenkreuz commented Oct 26, 2016

Having animated route transitions would be nice, as this is one of the many benefits through client-side routing. Doing so should be left up to the user in my opinion (some people prefer CSS transitions over more fine-grained control with react-motion etc). As I understand it, one would need to modify the top-level App component.

The client entry file seems to look for a globally assigned __NEXT_DATA__.app variable but I can't find any documentation on that.

@dlindenkreuz dlindenkreuz changed the title Animated route transitions Top-level app component and animated route transitions Oct 27, 2016
@luisrudge
Copy link

as commented in #104, I think a good solution would be supporting a _layout.js file like the _error.js.

@dlindenkreuz
Copy link
Contributor Author

@luisrudge to me, this also seems to be the most coherent way.

@sedubois
Copy link
Contributor

sedubois commented Nov 1, 2016

Is this duplicate of #50?

@dlindenkreuz
Copy link
Contributor Author

This issue aims for a single modifiable root component whereas in #50, the complex strategy of having multiple roots is discussed. With the latter, route transition animations could be hard or at least different to implement.

I would answer your question with no, but feel free to comment if merging the issues would make sense.

@sedubois
Copy link
Contributor

sedubois commented Nov 1, 2016

Thanks, indeed having an external template file sounds simpler than multiple roots. It might help to reword the issue to speak in broader terms of a shared template which doesn't reload? (to keep header, footer, CSS with transitions, Redux Provider, ApolloProvider, etc)

I would also not call such a file _layout, because as mentioned above it could help with other things than visual layout (such as maintaining global state).

@richardnias
Copy link

I think this would be good. It's already fairly easy to implement I think - see my example of a simple PageFactory component, which is just a higher order component that I wrap my pages in (example). (note this example isn't complete, as ideally PageFactory would implement getInitialProps which would call the equivalent method of the child component).

It would definitely be better to have an optional component that wraps pages automatically, to save users reimplementing something like PageFactory and having to remember to wrap each page.

@eirikurn
Copy link

eirikurn commented Nov 23, 2016

How about allowing each folder in pages/ to have a _layout.jsx module. This module exports a layout component for pages in that folder. Nested folders could have nested layout components, a la react-router. The life cycle of these layout component would work like in react router, so they could maintain state across page loads and perform page transitions.

This method is inspired by gatsby, which uses React Router behind the scenes.. But next could take this method further, supporting getInitialProps at every level, e.g. load navigation, sidebar data.

Another interesting point; this pattern breaks in gatsby when nested layouts don't match urls. But with Programmatic API (#291), it would be easy to work around that.

@CompuIves
Copy link
Contributor

I made a version which uses the _layout.js from the root folder of pages. I want to change some things before I'll make a PR for this. The suggestion by @eirikurn is also very interesting, I'll try to get this working as well.

@CompuIves
Copy link
Contributor

With this _layout file we have the possibility to store the global state on the layout component which in turns makes it easier to build an SPA with next.

@arunoda
Copy link
Contributor

arunoda commented Dec 2, 2016

Guys, animations might now have been worked before since we load the same JS module multiple times in every page transition. See: #253

We've fixed that with v1.2.x. So, I assume animations will work between pages without any additional work.

@L-A
Copy link

L-A commented Dec 2, 2016

@arunoda Persistent modules aren't the whole story. On the UI side, animating relies on components and their states being diff'ed on render.

v1.2.3 still renders a new stateless component from the top of the tree instead – nothing is preserved under the <AppContainer> on page change.

This doesn't allow a component to be in a certain state, then animate to another when a new page is rendered. It's always discarded and re-rendered as a new component.

@souporserious
Copy link
Contributor

souporserious commented Dec 27, 2016

Is this now possible in 2.0.0 beta? I'm looking to do a transition similar to React Router's example here: https://react-router.now.sh/animated-transitions or even something simple like this just to get a starting point would be awesome http://stackoverflow.com/a/40479463/1461204

@mmmeff
Copy link

mmmeff commented Jan 11, 2017

@rauchg Given the new layout example added last week still exhibits re-rendering on page-change, how does the team plan to solve this problem?

@rauchg
Copy link
Member

rauchg commented Jan 11, 2017

Client side route transitions are tricky. In order for them to work properly, the page you're linking from has to be certain that the next page has already been loaded.

I think one way to enable this could be by leveraging the imperative prefetching API:

<a> onClick = () => {
  prefetch('/something')
  .then((page) => {
    // render the `page` component & trigger transition
    // then push before or after the transition completes, up to you
  })
}</a>

That said, we'd also need some way to handle the pop event in a custom way.

If you really want smooth transitions without introducing so much complexity (that has to do with the uncertainty of pages being loaded or not) you can make the routes that need transitions a single page.

This is what the nextgram example shows (https://github.com/zeit/nextgram). A certain route can take on different URLs by calling this.url.push. componentWillReceiveProps then will get called as the url object changes. New entries are introduced into the history stack, but they're all associated with that one page.

image

Then notice in render:

image

This is how we're able to show the photo as a modal, and if you hard-refresh, as a permalink page.

Another way of doing this, declaratively, is by using as in <Link>:

<Link href="/index?section=1" as="/section-1">Section 1</Link>
<Link href="/index?section=2" as="/section-2">Section 2</Link>

Since it'll end up being the same component and the same state, you can transition easily.

In both cases you'll want to make sure that /section-1 and /section-2 point back to the index page by using the server API, so that server-rendering works correctly.

@rauchg
Copy link
Member

rauchg commented Jan 11, 2017

I think that's a good-enough workaround for now. It should give you the same experience you see in that React Router example because it assumes that the 4 components are already loaded.

If you have any other ideas, please let me know. For our use cases the transition stuff is not a huge priority, but always open to elegant solutions.

@arunoda
Copy link
Contributor

arunoda commented Jan 11, 2017

@rauchg I've updated nextgram to use Next 2 and custom routing API.
See: #693 (comment)

Here's more information about the re-rendering.

  • If you change an actual page, UI will be re-rendered. There's no direct way to fix that.
  • If you change the URL but keep the same page (via custom router API), next.js won't re-render the page. With that, you can achieve animation and other stuff.

See this repo for how it render a modal with a route change: https://github.com/arunoda/nextgram

@L-A
Copy link

L-A commented Jan 11, 2017

Agreed that the routing API makes it possible to do transitions as needed. A top-level component is also available now. And the two are already documented.

To me, that rounds up the issue. 🙌

@timneutkens
Copy link
Member

Closing. Will re-open when needed.

@ngocketit
Copy link

I'm about to port one of the existing project to Next and one of the requirements is that we need to have animation when transitioning between pages that share the same layout (with navigation & footer) but different main content. So having this is a must for the project (as required from the product owner). But if that can't not be done with the current version, I'll need to look for other solutions, which is bad since Next is very nice to work with.

@L-A
Copy link

L-A commented Jan 18, 2017

@ngocketit If you read up a, bit, there are now reliable ways to do it. Make sure to include "next": "^2.0.0-beta" for now and you should be good to go.

@ngocketit
Copy link

@L-A Thank you for your reply! However, I think the custom routing trick mentioned above is still like a hack because it's still just one page. Lets say, for example, I have 3 paths: /, /channels and /channels/:channelId which all share the common layouts with exactly the same navigation & footer. Now I want to have a transition animation when I switch between them. With the custom routing approach, I'll need to map those 3 paths to the same page and render different content based on the path name. That means, I'll lose the benefit of the page concept in Next as I have only one page now and no more, for example, page prefetching. It'd be great if I still can have 3 separate pages for those 3 different paths but the "pages" only map to the main content, not the whole layout.

@andy-mcdonald
Copy link

Lots of really nice improvements in v5 (congrats to the team) but in reading the blog post, it seems that the top-level / persistent App component which would enable complex, animated page transitions did not make it into this release - can anyone confirm that this is indeed the case?

Obviously a lot of work has gone into v5 so hopefully, now it is out, a solution won't be too far away.

@tz5514
Copy link

tz5514 commented Feb 7, 2018

@andy-mcdonald I think it won't take too much time since v5 was released. Let's look forward to it!

@postor
Copy link

postor commented Feb 12, 2018

I made page transition work, using Router.beforeRoute and wrapper https://github.com/postor/next-page-transition
screenshot

@mocheng
Copy link

mocheng commented Feb 24, 2018

@postor Nice try. By saving HTML fragment from innerHTML and inject it back with dangerouslySetInnerHTML, it works even with next.js version before v5. But, direct manipulation of HTML might bring other problems. Really hope this would get a official solution after v5.

@rashidul0405
Copy link
Contributor

rashidul0405 commented Feb 26, 2018

@timneutkens thanks for pointing to this from #3891. This top-level layout component really needed for many use cases like a dynamic menu(from DB or Redis) that just load once etc.

@tz5514
Copy link

tz5514 commented Feb 26, 2018

@rashidul0405 #3552 It can resolve this problem nicely, but core contributers are not ready for reviewing it yet.

@chanwaiyu
Copy link

Any updates on the issues?

@OmarZeidan
Copy link

Hello floks,

Any updates on this?

@krazyjakee
Copy link

krazyjakee commented Mar 21, 2018

@OmarZeidan I'm guessing all eyes are on this PR? #3552

The animation discussion really throws me off what exactly is being achieved here since many issues referring to layouts were closed as duplicates of this issue. My requirement is a layout that doesn't remount after a route changes ensuring only child components are updated. I'm presuming the animation would be a test case for that or something?

@eweilow
Copy link

eweilow commented Mar 21, 2018

@krazyjakee From my own experimentation, the React reconciler seem to regard different components (pages) as entirely different DOM trees even if they share some child components.

Animations are a good test case as they require persistent DOM nodes, but it only highlights the underlying issue and #3552 might be a more general solution than this PR 👍

@Ionut-Milas
Copy link

Hi guys,
Any update on how can be prevented the reload of a top-level component (layout) ?

@tz5514
Copy link

tz5514 commented Mar 24, 2018

@Ionut-Milas #3552 (comment)

@mellster2012
Copy link

@hugotox 's solution seems to be by far the easiest if you need animated components. The base code is minimal and can be extended by any component. Thx!

@chanlito
Copy link

After reading all the comments I have no clue how to do page transition in next.js, coming from nuxt.js which is so easy by just adding some css.

@pozylon
Copy link

pozylon commented Apr 19, 2018

@chanlito just use react-transition-group inside _app.js, like this:

import App, { Container } from 'next/app';
import React from 'react';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
import PageLayout from '../components/PageLayout';

export default class MyApp extends App {
  render() {
    const { Component, pageProps } = this.props;
    return (
      <Container>
        <PageLayout>
          <TransitionGroup>
            <CSSTransition
              key={this.props.router.route}
              classNames="fade"
              timeout={1000}
            >
              <Component {...pageProps} />
            </CSSTransition>
          </TransitionGroup>
        </PageLayout>
      </Container>
    );
  }
}
.fade-enter {
  opacity: 0;
}

.fade-enter-active {
  opacity: 1;
  transition: opacity 0.3s;
}

.fade-exit {
  display: none;
}

@pozylon
Copy link

pozylon commented Apr 19, 2018

i forgot to mention you need to install 6.0.0 canary-4 for that to work

@chanlito
Copy link

@pozylon Thanks, I did something similar but I was using react-transition-group version 1 which didn't work.

@pozylon
Copy link

pozylon commented Apr 19, 2018

Here is my pull request if you want to see the full example: #4177

@corysimmons
Copy link
Contributor

corysimmons commented Apr 23, 2018

For everyone jumping to bottom of page for updates: this functionality seems to be in https://github.com/zeit/next.js/releases/tag/6.0.0-canary.4 (released in v6 unless it gets removed for some reason), and @pozylon has full usage example code here: https://github.com/pozylon/next.js/tree/ad30b735389766ad022cc58b17b86ed11c15a866/examples/with-react-transition-group

@designspin
Copy link

I have a custom _document.js, can I use _app.js with this?

@nwalters512
Copy link
Contributor

Now that 6.0 has officially been released, check out next-page-transitions. I built it as a thin wrapper around react-transition-group that similarly applies CSS classes that you give meaning to. It handles making sure only one page is mounted at a time and has built-in support for React-suspense-like page loading indicators.

@designspin yep, they work fine with each other, I'm using both in my app!

@lock lock bot locked as resolved and limited conversation to collaborators May 17, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.