{"id":21241223,"url":"https://github.com/icyjoseph/hidden-attribute","last_synced_at":"2025-07-10T20:30:53.518Z","repository":{"id":37739984,"uuid":"190280755","full_name":"icyJoseph/hidden-attribute","owner":"icyJoseph","description":"Uses the hidden html attribute to pre-load content using React.","archived":false,"fork":false,"pushed_at":"2023-01-09T07:52:25.000Z","size":1139,"stargazers_count":9,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-05T17:05:36.036Z","etag":null,"topics":["hidden","parcel","react","react-suspense","reactjs"],"latest_commit_sha":null,"homepage":"https://hidden-attribute.surge.sh/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/icyJoseph.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-06-04T21:17:54.000Z","updated_at":"2024-08-27T08:01:40.000Z","dependencies_parsed_at":"2023-02-08T09:16:54.447Z","dependency_job_id":null,"html_url":"https://github.com/icyJoseph/hidden-attribute","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/icyJoseph/hidden-attribute","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icyJoseph%2Fhidden-attribute","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icyJoseph%2Fhidden-attribute/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icyJoseph%2Fhidden-attribute/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icyJoseph%2Fhidden-attribute/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/icyJoseph","download_url":"https://codeload.github.com/icyJoseph/hidden-attribute/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/icyJoseph%2Fhidden-attribute/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264652628,"owners_count":23644292,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hidden","parcel","react","react-suspense","reactjs"],"created_at":"2024-11-21T00:54:59.812Z","updated_at":"2025-07-10T20:30:53.156Z","avatar_url":"https://github.com/icyJoseph.png","language":"JavaScript","readme":"# Hidden Attribute\n\nDemonstrates how to take advantage of the HTML attribute `hidden`.\n\n## Demo\n\nDemo available [here](https://hidden-attribute.surge.sh/).\n\n## About\n\n[Article.](https://medium.com/evolve-technology/hide-that-da6264a7e1f)\n\nThis repository exists for educational purposes only.\n\nIt uses [nes.css](https://nostalgic-css.github.io/NES.css/) as styling library.\n\nIt uses [Parcel](https://parceljs.org/) to bundle the application.\n\nConsumes data from [CoinDesk](https://www.coindesk.com/api) and [PokeApi](https://pokeapi.co/).\n\n## Explanation\n\nEvery time a React component mounts, updates or is unmounted, React executes two phases:\n\n- A render phase 📖\n- And a commit phase 📝\n\nThere is a cause-effect relation between these, however, when Concurrent Mode comes out, you'll be able to opt-in to have many render phases before a commit phase actually kicks in. The actual details of this are not clear, so do not think about them just yet.\n\nDuring the commit phase React makes changes to the DOM, after this, the [Browser's job](https://developers.google.com/web/fundamentals/performance/rendering/) is to apply CSS styles, layout calculations, paint pixels and compose your elements into layers.\n\nThat's a lot of work!\n\nLuckily, there is a DOM attribute called `hidden`, which you can use to get React to commit changes to the DOM, while also telling the browser not to run its own work, just yet.\n\nIt is equivalent to display none, in fact, setting a display property on hidden elements overrides the attribute!\n\nEven though [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden) recommends not to use hidden to hide panels, it is a great example to show how to take advantage of this attribute.\n\nIn this demo, there are three tabs:\n\n- The Landing tab, simply displays a message.\n- The Bitcoin tab, loads data from [CoinDesk](https://www.coindesk.com/api) and shows it in a [Victory Chart](https://formidable.com/open-source/victory/docs).\n- The Pokémon tab, loads Pokémon from [PokeApi](https://pokeapi.co/) and shows their sprites.\n\nEach tab is a separate `JavaScript` bundle thanks to React's lazy. Since the `Bitcoin` tab uses `Victory` Charts, this bundle is bound to be heavy and will most likely load last. The demo also uses [Parcel](https://parceljs.org) to bundle the whole application with zero configuration.\n\nAs soon as the page loads, the three bundles are fetched. Because the demo uses `lazy + Suspense`, at this point, all three tabs do a `fallback`. As far as the `Tabs` handler is concerned, the job is done, React is now handling the asynchronous loading.\n\nAfter each bundle is loaded, it is parsed, then the actual component is mounted and its own life cycles methods are executed. On mount, the landing page does nothing special, however, in the Pokemon tab, `fire` type Pokémons are queried, and in the bitcoin tab a request for the BTC/SEK rate during the last month is created.\n\nAll of this is done before the user has switched tabs. Most likely, it all happens as they read the landing page message.\n\nTo see that the browser is not actually spending resources in loading hidden elements, in the Pokémon tab, elements with an even index order load their Pokémon sprite as `CSS` background, while the ones with an odd index load the sprite as an `img` HTML element.\n\nNow one can observe that React commits its work to the DOM, and Pokémon with odd indeces fetch the `src` attribute in the `img` element, but Pokémon with even indeces do not, because it is CSS and the hidden attribute tells the browser to not run it's work chain, just yet.\n\nOpen the network tab in your browser dev tools to see for yourself, as soon as you switch to the Pokémon tab, the even indeces images are fetched. Notice that the Bitcoin data is also fetched upon page loading.\n\nWhat if you switch tabs quickly? You'll either see the `fallback`, or simply see your rendered component.\n\n## Implementation\n\nAt the root of the React three in the demo we have:\n\n```jsx\nfunction Tab({ title, children }) {\n  return (\n    \u003c\u003e\n      \u003ch3\u003e{title}\u003c/h3\u003e\n      \u003cdiv\u003e{children}\u003c/div\u003e\n    \u003c/\u003e\n  );\n}\n\nfunction App() {\n  return (\n    \u003cTabs\u003e\n      \u003cTab label={\u003cLandingLabel /\u003e} title=\"Landing\"\u003e\n        \u003cSuspenseLanding /\u003e\n      \u003c/Tab\u003e\n      \u003cTab label={\u003cBitCoinLabel /\u003e} title=\"Bitcoin\"\u003e\n        \u003cSuspenseBitcoin /\u003e\n      \u003c/Tab\u003e\n      \u003cTab label={\u003cPokemonLabel /\u003e} title=\"Pokemon\"\u003e\n        \u003cSuspensePokemon /\u003e\n      \u003c/Tab\u003e\n    \u003c/Tabs\u003e\n  );\n}\n```\n\nWhere the `lazy + Suspense` are defined like so:\n\n```jsx\nconst LazyLanding = React.lazy(() =\u003e import(\"./containers/Landing\"));\n\nfunction SuspenseLanding({ ...props }) {\n  return (\n    \u003cReact.Suspense fallback={\u003cdiv\u003eLoading...\u003c/div\u003e}\u003e\n      \u003cLazyLanding {...props} /\u003e\n    \u003c/React.Suspense\u003e\n  );\n}\n```\n\nTo implement the desired behavior, write the `Tab` handler as such:\n\n```jsx\nfunction Tabs({ children }) {\n  const [current, setCurrent] = React.useState(0);\n\n  const controls = React.useMemo(\n    () =\u003e\n      React.Children.map(children, ({ props: { label } }, index) =\u003e (\n        \u003cbutton key={index} onClick={() =\u003e setCurrent(index)}\u003e\n          {label}\n        \u003c/button\u003e\n      )),\n    []\n  );\n\n  return (\n    \u003c\u003e\n      \u003cdiv\u003e{controls}\u003c/div\u003e\n      {React.Children.toArray(children).map((child, index) =\u003e (\n        \u003cdiv key={index} hidden={index !== current}\u003e\n          {child}\n        \u003c/div\u003e\n      ))}\n    \u003c/\u003e\n  );\n}\n```\n\nThis approach, begins the async `lazy + Suspense` loading of each tab, and then renders and commits each children to the DOM. However, those not currently selected children are `hidden` in the DOM, only the current is displayed.\n\nAnother approach is to:\n\n```jsx\nfunction Tabs({ children }) {\n  const [current, setCurrent] = React.useState(0);\n\n  const controls = React.useMemo(\n    () =\u003e\n      React.Children.map(children, ({ props: { label } }, index) =\u003e (\n        \u003cbutton key={index} onClick={() =\u003e setCurrent(index)}\u003e\n          {label}\n        \u003c/button\u003e\n      )),\n    []\n  );\n\n  return (\n    \u003c\u003e\n      \u003cdiv\u003e{controls}\u003c/div\u003e\n      {React.Children.toArray(children).map(\n        (child, index) =\u003e index === current \u0026\u0026 \u003cdiv key={index}\u003e{child}\u003c/div\u003e\n      )}\n    \u003c/\u003e\n  );\n}\n```\n\nUnfortunately, this actually prevents the tabs from being `lazy + Suspense` loaded, rendered and commited to the DOM by React. With this implementation, switching tabs triggers the `fallback` on Suspense, and will make users wait until it resolves, renders and commits to the DOM, after which data is fetched and updates the component, triggering render and commit once again.\n\nThe code for this demo lives in this [repository](https://github.com/icyJoseph/hidden-attribute).\n\nEnjoy!\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ficyjoseph%2Fhidden-attribute","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ficyjoseph%2Fhidden-attribute","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ficyjoseph%2Fhidden-attribute/lists"}