primefaces / primereact

The Most Complete React UI Component Library
https://primereact.org
MIT License
6.24k stars 964 forks source link

Flash of unstyled content (FOUC) #5188

Open gslopez opened 8 months ago

gslopez commented 8 months ago

Describe the bug

When using built in PR theme seeing a flash of un-styled content: https://en.wikipedia.org/wiki/Flash_of_unstyled_content

I have this super simple code

import React, {useState} from 'react';
import {TabMenu} from 'primereact/tabmenu';
import {MenuItem} from 'primereact/menuitem';

type NavbarProps = {
    onPageSelected?: (page: string) => void,
    menuItems?: MenuItem[],
}

export const Navbar = ({menuItems, onPageSelected}: NavbarProps) => {
    const [activeIndex, setActiveIndex] = useState(0)

    return (
        <TabMenu
            activeIndex={activeIndex}
            model={
                menuItems
            }
            onTabChange={(e) => {
                setActiveIndex(e.index);
                onPageSelected?.(e.value.id as string)
            }}
        />
    )
}

When the page first loads, it looks like this first for like 0.5 seconds

Screenshot 2023-10-29 at 3 24 56 PM

and then it changes to how it should really look

Screenshot 2023-10-29 at 3 24 47 PM

After some debugging I found out it is because when the component is loaded, it loads this global style

Screenshot 2023-10-29 at 3 27 42 PM

https://github.com/primefaces/primereact/blob/master/components/lib/tabmenu/TabMenuBase.js#L79 https://github.com/primefaces/primereact/blob/master/components/lib/componentbase/ComponentBase.js#L664

I recorded this video with the issue on the codesandbox i created

https://github.com/primefaces/primereact/assets/2165906/01eb40c2-e062-40ee-8934-3355b858f502

I would expect either

  1. The component should be auto-sufficient (i.e it looks properly on first load)
  2. To have a way to import the components css globally directly, next to the theme and min imports (something like import "primereact/components/tabmenu.css

Reproducer

https://codesandbox.io/s/primereact-test-forked-xsr6gj?file=/src/index.js

PrimeReact version

10.0.7

React version

18.x

Language

TypeScript

Build / Runtime

Create React App (CRA)

Browser(s)

No response

Steps to reproduce the behavior

No response

Expected behavior

No response

melloware commented 8 months ago

I agree I have seen this same flash flicker when it renders.

This is known as FOUC https://en.wikipedia.org/wiki/Flash_of_unstyled_content and maybe all those base styles need to be extracted into a primereact.css like you said so if we want we can include it in the page and not need each component to dynamically add the CSS to the HEAD

melloware commented 8 months ago

@gslopez check this out can you try this: https://codesandbox.io/s/primereact-test-forked-9cl6y8?file=/src/styles.css

I merged all of the base styles into a single CSS file to load. If this meets your needs I think I can propose something clever.

melloware commented 8 months ago

Updated with more complete version of the final CSS: https://codesandbox.io/s/primereact-test-forked-9cl6y8?file=/src/styles.css

gslopez commented 8 months ago

hey @melloware! Thanks for the quick answer. Yeah, if I add import "./styles.css"; to that codesandbox it seems to be working fine. I think this is good enough for my current project 🙏

alaordev commented 8 months ago

@melloware any date prediction of 10.0.10 release? Facing the same issue here

melloware commented 8 months ago

This fix has not been included in 10.0.10 yet as PrimeTek is still discussing my fix. For now you can use the CSS in the reproducer above to temporarily fix your issue.

melloware commented 7 months ago

NextJS 14 reproducer: https://stackblitz.com/edit/stackblitz-starters-mqapwn?file=app%2Fglobals.css,app%2Flayout.tsx

axlerk commented 5 months ago

Facing the same issue while migrating to a major version 8 → 10 on my project. Any news here? It's look like a blocker for me

melloware commented 5 months ago

You can use my workaround posted above

lsegal commented 5 months ago

You can use my workaround posted above

@melloware do you mean this?

Updated with more complete version of the final CSS: https://codesandbox.io/s/primereact-test-forked-9cl6y8?file=/src/styles.css

This codesandbox still shows a FOUC on first load. It's a little less noticeable but it still occurs. A little difficult to show with Chrome Inspector (it limits how big the Visual Preview window can be) but the following shows the list elements along with the final rendered content 1ms later:

image image

melloware commented 5 months ago

Yes it doesn't eliminate it it just makes it less jarring by preloading CSS.

axlerk commented 3 months ago

Is it possible to somehow escalate this issue? Add more likes or idk :) Looks important.

melloware commented 3 months ago

@axlerk PrimeTek is aware of this issue its on their radar.

emirhancopoglu commented 2 months ago

any news?

thingersoft commented 2 months ago

any news?

+1

cdllc commented 1 month ago

+another

emirhancopoglu commented 1 month ago

+++

starlocater commented 1 month ago

++++

axlerk commented 1 month ago

@melloware could you update the workaround for version 10.6.6 please? Until we have a solution

melloware commented 1 month ago

@axlerk i assume you means some styles are missing from my giant CSS file?

acho1833 commented 1 month ago

Now I get what @melloware is saying now, PrimeReact adds the styles 'after' the component have already been rendered. See the left side of the screenshot and you'll notice that 'base', 'global', 'splitbutton', are being added dynamically. On the right side of the screen, you don't and that's why it's showing FOUC.

image

I think I can come with decent 'workaround' solution without putting those css somewhere. My idea is the following. Please let me know if my logic can be improved or flawed.

  1. Make the body tag to be display:none in the Root Layout component. Something like
    <body style={{display: "none"}}>
  2. Add a script tag, that checks for added styles with the attribute 'data-primereact-style-id'. Check that at least 3 of them exists (Imagine those are 'base', 'global', 'common') and maybe add another 50ms and then change the body style back to 'block'/'flex'/or whatever...

Hopefully this will work as temporary workaround...

melloware commented 1 month ago

@acho1833 yes you understand the issue now. PrimeTek I think is working on a long term solution after their next big refactor.

acho1833 commented 1 month ago

My theory seems to work and I think it's good to go now. To me, this is enough until the fix and looks perfect to me.


export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <head>
        <style
          dangerouslySetInnerHTML={{
            __html: `
              body {
                display: none;
              }
            `,
          }}
        />
        <script
          dangerouslySetInnerHTML={{
            __html: `
                document.addEventListener('DOMContentLoaded', function() {
                    const intervalId = setInterval(() => {
                        const elements = document.querySelectorAll('head > style[data-primereact-style-id]');
                        if (elements.length >= 3) {
                            document.body.style.display = 'block';
                            clearInterval(intervalId);
                        }
                    }, 50);
                });
            `,
          }}
        />
      </head>
      <body className={inter.className}>
        <PrimeReactProvider>{children}</PrimeReactProvider>
      </body>
    </html>
  );
}

@melloware looking forward for next release. Is that real soon? know any rough ETA?

melloware commented 1 month ago

I don't work for PrimeTek so I don't know the internal details of their schedule or roadmap.