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

Introduce app-root #5423

Merged
merged 18 commits into from
Oct 5, 2023
Merged

Introduce app-root #5423

merged 18 commits into from
Oct 5, 2023

Conversation

goplayoutside3
Copy link
Contributor

@goplayoutside3 goplayoutside3 commented Sep 28, 2023

Package

app-root

Linked Issue and/or Talk Post

FEM packages structure discussion: #5089
Toward: #5429

Describe your changes

  • Create app-root package
  • Use Next.js 13 App Router for /about, /users, /groups/ and /projects
  • Setup Grommet theme and styled-components stylesheet
  • Import ZooHeader and ZooFooter into layout.js

How to Review

app-root can be run locally with yarn dev

Please read through ADR 54 to check for relevant information and typos.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is there a .gitignore file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are .gitignore files in app-project, app-content-pages, and lib-classifier. Should there not be one here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not sure. This one seems to be mostly duplicating the one at the root of the monorepo.

@goplayoutside3
Copy link
Contributor Author

Okay I think I solved the FOUC! I ended up creating a test project outside of the monorepo workspace that included the same dependencies and file structure here. During that process, I finally saw a warning that Grommet is incompatible with styled-components v6, so I bumped the version back to v5.

@eatyourgreens when you have a minute, could you take a look at this branch locally, try yarn dev and see if a FOUC is visible? I think it'd be helpful to double check this branch on more than one machine.

I plan to add an ADR to this PR before marking it ready for review, and will elaborate on next steps needed for app-root in #5429.

@coveralls
Copy link

coveralls commented Oct 2, 2023

Coverage Status

coverage: 82.153%. remained the same when pulling 0647d78 on app-root into 057599d on master.

@@ -0,0 +1,27 @@
'use client'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, so the server sends an unstyled page to the browser, then styles are built in the client?

Copy link
Contributor

@eatyourgreens eatyourgreens Oct 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe not. The downloaded HTML includes an inline stylesheet. Vendor prefixes can definitely be removed here.

If we could move all this into an external CSS file, that would cost us less to build and serve, but I don't think that's possible with styled components and Next.js. At the moment, the server has to rebuild the CSS whenever the page is rebuilt (#3380), so whenever content changes. However, the CSS only changes once a week, when we deploy.
View Source for the new app, showing an inline <style> stylesheet in the page head.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 this stylesheet fixes the page styling issue.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Breakpoints should be 48rem, not 768px, but that's probably a bug in the Grommet theme.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is odd. I built the app a second time. After the second build, I don't see the server-side stylesheet in the HTML, so it's unstyled again when it first loads.

Screen.Recording.2023-10-02.at.12.59.12.mov

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems ok after running the bootstrap script and building for a third time. 🤷‍♂️

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to know, thanks for double checking!

return <>{styles}</>
})

if (typeof window !== 'undefined') return <>{children}</>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This checks if the code is running in a browser, but the file specifies use client, so that's confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also a bit confused. I used the example from https://nextjs.org/docs/app/building-your-application/styling/css-in-js#styled-components, but don't totally understand why this line is included.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to know I'm not the only one that's confused by how this works. 😄

// x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet())

useServerInsertedHTML(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hook name is confusing in a client-side component.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although this is a client component, you noted earlier the downloaded HTML includes an inline stylesheet. Client components still run once on the server, and then again in the browser. This code is copied from the styled-components example: https://nextjs.org/docs/app/building-your-application/styling/css-in-js#styled-components

Do you recommend changing the function name?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hook's name is set by Next.js, so that's fine. I was just confused as to how this component works.

@eatyourgreens
Copy link
Contributor

eatyourgreens commented Oct 2, 2023

80k of JS in the browser before we've even begun to build anything. 😞

Next.js build report for the stub app, with the client-side bundle already at 80k even though the page content is HTML-only.

EDIT: those must be gzipped sizes. Next.js is actually shipping 600k of client JS!

The browser dev console, showing JS download sizes for the new app, in the network panel.

@eatyourgreens
Copy link
Contributor

eatyourgreens commented Oct 2, 2023

I'm running this locally, and the links in ZooHeader are all external links to www.zooniverse.org. For this app, you'll need to build your own navigation menu, using next/link for the links that are handled by Next.js.

@eatyourgreens
Copy link
Contributor

eatyourgreens commented Oct 2, 2023

The local app uses HTTP. At some point we'll also need to add a custom dev server to handle HTTPS, so that we can authenticate to Panoptes in development.

EDIT: we'll need a Dockerfile too, so that we can build and deploy. Nothing urgent, but making sure that we've got these on our radar so that we don't miss them out.

@eatyourgreens
Copy link
Contributor

eatyourgreens commented Oct 2, 2023

I think the large bundle size is my fault. I removed the module entry point for @zooniverse/react-components in #5325. That probably broke tree-shaking in Webpack. This PR is probably exporting the whole library in the bundle, even though it only uses ZooHeader and ZooFooter.

#5430 might fix it.

@eatyourgreens
Copy link
Contributor

eatyourgreens commented Oct 2, 2023

I've published @zooniverse/react-components 1.6.3, which might lower the bundle size. If not, you can import just the components that you need from version 1.6:

import ZooHeader from '@zooniverse/react-components/ZooHeader'
import ZooFooter from '@zooniverse/react-components/ZooFooter'

In Next.js 13.5, you can also try optimising package imports in next.config.js.

@goplayoutside3
Copy link
Contributor Author

Re these points:

I'm running this locally, and the links in ZooHeader are all external links to www.zooniverse.org. For this app, you'll need to build your own navigation menu, using next/link for the links that are handled by Next.js.

The local app uses HTTP. At some point we'll also need to add a custom dev server to handle HTTPS, so that we can authenticate to Panoptes in development.

I've added them to the running list in #5429 which might be converted to a project board 👍

goplayoutside3 and others added 2 commits October 2, 2023 21:33
@eatyourgreens
Copy link
Contributor

Changes here look good if the aim of this PR is to get the basic theme styles working. 👍

@goplayoutside3
Copy link
Contributor Author

Changes here look good if the aim of this PR is to get the basic theme styles working. 👍

Cool! I'm going to add documentation to the Readme and an ADR. I'll change this to ready for final review in the next 24 hrs.

docs/arch/adr-54.md Outdated Show resolved Hide resolved
@eatyourgreens
Copy link
Contributor

eatyourgreens commented Oct 5, 2023

Sorry, one other comment. Have you any thoughts on how this app will use the staging vs. production Panoptes API?

At the moment, production projects are served from /projects/production and staging from /projects/staging. We rewrite request URLs to point to the appropriate path in the Next.js build, depending on whether the request is for a Panoptes staging project or a Panoptes production project.

/*
Project pages are served from /projects/staging/[owner]/[project]
and /projects/production/[owner]/[project]
*/
url.pathname = `/${panoptesEnv}${pathname}`
return NextResponse.rewrite(url)

Obviously, with this app, we'll have staging/production users too. Also a staging API homepage and a production API homepage. On the homepage, I think that Featured Projects is the only place where the Panoptes API would be used on the server. All the other Panoptes requests are made by the browser (I think.)

packages/app-root/package.json Outdated Show resolved Hide resolved
packages/app-root/package.json Outdated Show resolved Hide resolved
packages/app-root/src/app/layout.js Outdated Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this file do?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, is it a Visual Studio thing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct! I'll delete this file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh oh, this file is actually responsible for resolving paths. When I delete it there's an error:

Module not found: Can't resolve '@/components/RootLayout'
> 1 | import RootLayout from '@/components/RootLayout'

@eatyourgreens
Copy link
Contributor

I've got a lot of questions, but I don't think there's anything that would stop this from merging. What do you think?

@eatyourgreens
Copy link
Contributor

I ran a Lighthouse report in Chrome, and it scored us low on performance, because there's so much JS. Total JS execution time is ~3s on a mobile device.

Lighthouse scores for the new app. Performance 65, Accessibility 100, Best Practices 100, SEO 95.

@github-actions github-actions bot added the approved This PR is approved for merging label Oct 5, 2023
@eatyourgreens
Copy link
Contributor

The weird thing is, there's nothing in the pages that uses JS. If you disable JS in your browser, then load http://localhost:3000, the new home page loads just fine, with CSS styles inlined into the page head.

@eatyourgreens
Copy link
Contributor

I downgraded #5411 to Next 13.5.3 and it still won't build the content pages storybook, so the problem might be the latest update to Storybook (7.4.6.)

@eatyourgreens
Copy link
Contributor

I'm going to split #5411 into one PR that fixes the build warnings for Next 13.4, and a second PR that upgrades to Next 13.5.

@goplayoutside3
Copy link
Contributor Author

goplayoutside3 commented Oct 5, 2023

I ran @next/bundle-analyzer locally with app-root and here's the output. I'm not surprised to see Grommet and React as large libraries, but I am surprised to see i18next included. Does the fact that app-root lives in a monorepo affect bundle size here?

Node.js
Screenshot 2023-10-05 at 11 27 51 AM

Client
Screenshot 2023-10-05 at 11 28 04 AM

@eatyourgreens in your network screenshot, what is the significance of "transferred" versus "size"?

image

@goplayoutside3
Copy link
Contributor Author

goplayoutside3 commented Oct 5, 2023

Oh wait, I just answered my own question. Even though i18next isn't used directly in app-root, ZooHeader and ZooFooter use it for translations. And that's also why the translation json dictionaries are in the client screenshot above.

@eatyourgreens
Copy link
Contributor

@eatyourgreens in your network screenshot, what is the significance of "transferred" versus "size"?

I think Next.js gzips by default, in which case that's showing gzipped size vs. actual size.

@goplayoutside3
Copy link
Contributor Author

I think Next.js gzips by default, in which case that's showing gzipped size vs. actual size.

Thanks!

It's interesting that I see different sizes when using Firefox versus Chrome. For instance, when I build and run app-root locally with Firefox, I see the exact screenshot you posted above.

However, this is the size displayed by an Incognito window in Chrome:
Screenshot 2023-10-05 at 11 53 18 AM

@goplayoutside3
Copy link
Contributor Author

goplayoutside3 commented Oct 5, 2023

Ah actually, it may just be a naming difference. In the bottom bar, Chrome "resources" is the actual size and "transferred" is the gzipped. Whereas in Firefox, "Size" is actual size and it's displayed directly next to total "transferred".

@goplayoutside3
Copy link
Contributor Author

So, just for my understanding - We're concerned about actual size of ~600kb versus gzipped ~200kb because the browser still has to uncompress the gzipped content to the size of 600kb and send it to the user?

@eatyourgreens
Copy link
Contributor

The browser downloads ~170kB, then unzips the received file, then runs it. So it's running 600kB of JS, which is why Lighthouse has a poor performance score. There's also a performance hit because unzipping takes time too.

@eatyourgreens
Copy link
Contributor

We should turn off gzip in production, because our problems with the CDN in the past were caused by FrontDoor trying to gzip resources that were already gzipped.

@eatyourgreens
Copy link
Contributor

Nice to see that imports from @zooniverse/react-components are tiny. I think that means that recent changes to support tree-shaking and modularise that library are paying off in terms of smaller bundles.

@eatyourgreens
Copy link
Contributor

eatyourgreens commented Oct 5, 2023

I wonder why react-dom.production.min.js is being bundled and downloaded twice. That seems wrong. Also, agreed about how much i18next contributes to bundle size!

EDIT: react-dom is being bundled from node_modules/next/dist/compiled/react-dom and also from node_modules/react-dom. I knew Next has its own bundled copy of React, but I didn't realise it bundled it up like this.

@goplayoutside3
Copy link
Contributor Author

goplayoutside3 commented Oct 5, 2023

Agreed it's frustrating react is bundled twice! As for i18next, the whole library is imported in i18n.js in lib-react-components as part of its config.

I'm not going to address either of those issues in this PR, but will definitely bring it up during next week's standup and/or document it 👍

@eatyourgreens
Copy link
Contributor

We could alias react to next/dist/compiled/react (and the same for react-dom.) Seems like a hack though. 🤔

@goplayoutside3 goplayoutside3 merged commit c9cc4f1 into master Oct 5, 2023
7 checks passed
@goplayoutside3 goplayoutside3 deleted the app-root branch October 5, 2023 18:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved This PR is approved for merging
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants