{"id":13452555,"url":"https://github.com/treshugart/react-shade","last_synced_at":"2025-07-08T05:39:50.010Z","repository":{"id":40003091,"uuid":"114755466","full_name":"treshugart/react-shade","owner":"treshugart","description":"Use the native Web Component Shadow DOM API declaratively in React.","archived":false,"fork":false,"pushed_at":"2022-12-09T17:41:16.000Z","size":2040,"stargazers_count":292,"open_issues_count":13,"forks_count":13,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-02T06:08:25.548Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://react-shade.netlify.com/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/treshugart.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-12-19T11:10:02.000Z","updated_at":"2025-02-11T15:50:01.000Z","dependencies_parsed_at":"2023-01-25T22:46:49.813Z","dependency_job_id":null,"html_url":"https://github.com/treshugart/react-shade","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/treshugart%2Freact-shade","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/treshugart%2Freact-shade/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/treshugart%2Freact-shade/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/treshugart%2Freact-shade/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/treshugart","download_url":"https://codeload.github.com/treshugart/react-shade/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248008630,"owners_count":21032556,"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":[],"created_at":"2024-07-31T07:01:27.428Z","updated_at":"2025-04-09T09:06:30.884Z","avatar_url":"https://github.com/treshugart.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# react-shade\n\n\u003e Use the native Shadow DOM API declaratively in React.\n\n- 🗜️ [1.7k](https://bundlephobia.com/result?p=react-shade@0.4.0)\n- 🌲 Browser-native CSS scoping on the client.\n- 🖥️ Simulated CSS scoping on the server.\n- ✍️ Write your CSS with strings, objects or functions.\n- 🥤 Works with the Shadow DOM polyfill (only needed for IE11 and pre-Chromium Edge).\n\n## Install\n\n```sh\nnpm install react-shade\n```\n\n## Why\n\nMost CSS solutions for React scope CSS by simulating it, but there are\nbrowser-based primitives that already do this for us.\n\nThis library exposes the imperative\n[Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM)\nAPI as a set of React components that you can use declaratively.\n\n## Usage\n\n```js\nimport React from \"react\";\nimport { render } from \"react-dom\";\nimport Root, { Slot, Style } from \"react-shade\";\n\nconst App = () =\u003e (\n  \u003cRoot\u003e\n    \u003cStyle\u003e\n      {{\n        \".totes-not-global\": {\n          fontWeight: \"bold\"\n        }\n      }}\n    \u003c/Style\u003e\n    \u003cspan className=\"totes-not-global\"\u003eThis will be bold.\u003c/span\u003e\n    \u003cSlot\u003e\n      \u003cspan className=\"totes-not-global\"\u003eThis will NOT be bold\u003c/span\u003e\n    \u003c/Slot\u003e\n  \u003c/Root\u003e\n);\n\nrender(\u003cApp /\u003e, window.root);\n```\n\nThis will produce something like:\n\n```html\n\u003c!-- Requires a wrapping node because it needs to have a node to attach\nthe shadow root to. --\u003e\n\u003cdiv\u003e\n  \u003c!-- This is where the slot content ends up (as light DOM). --\u003e\n  \u003cspan class=\"totes-not-global\" slot=\"slot-0\"\u003eThis will NOT be bold\u003c/span\u003e\n  #shadow-root\n  \u003cstyle\u003e\n    .totes-not-global {\n      font-weight: bold;\n    }\n  \u003c/style\u003e\n  \u003cspan class=\"totes-not-global\"\u003eThis will be bold.\u003c/span\u003e\n  \u003cslot name=\"slot-0\"\u003e\u003c/slot\u003e\n\u003c/div\u003e\n```\n\n### `Root`\n\nThe `Root` component creates an element with a `shadowRoot` attached to it. This\nis the outer-boundary for scoping, meaning nothing comes in, and nothing gets\nout. You can use it standalone, or with the other components.\n\n#### About the root `\u003cdiv /\u003e` node\n\nAttaching a shadow root requires a real DOM node. We don't want to reach up in\nthe hierarchy and mutate the DOM, so the `Root` component needs to generate a\nnode to attach a shadow to. This defaults to a `div`, but can be whatever you\nwant. This isn't something that will ever be able to change due to the nature of\nthe DOM and React.\n\n### `Slot`\n\nThe `Slot` component declares an inner-boundary for your shadow root. Anything\nplaced inside of a `\u003cSlot /\u003e` will not affect anything in the ancestor `Root`\nand the `Root` cannot affect anything in the `\u003cSlot /\u003e`.\n\n#### Why not just use `\u003cslot /\u003e`?\n\n`\u003cSlot /\u003e` was chosen because it's a more idiomatic way of declaring content\nwhere custom elements aren't being used. Underneath the hood, the `Slot` will\nportal the content back to the host so it gets distributed using the built-in\nalgorithms.\n\n```js\n\u003cSlot\u003e\n  \u003cspan className=\"totes-not-global\"\u003eThis will NOT be bold\u003c/span\u003e\n\u003c/Slot\u003e\n```\n\n### `Style`\n\nThe `Style` component may seem redundant when you can just use `\u003cstyle /\u003e` but\nit does a number of things.\n\n- If you need SSR, or simulated scoping, you should use `Style`.\n- If you want to use objects and functions to represent your CSS, then you\n  should use `Style`.\n- If you would like your styles to be minified with your standard JS tooling,\n  then you should use `Style`.\n- If you don't care about those things, you're free to use the standard `style`\n  tag.\n\nThe following is a very simple use case that uses only an object to represent\nyour CSS.\n\n```js\n\u003cStyle\u003e\n  {{\n    selector: {\n      style: \"property\"\n    }\n  }}\n\u003c/Style\u003e\n```\n\nYou can also specify functions that react to props that are passed to `Style`.\nBoth the set of rules and each property can be specified as a function. Whatever\n`props` that are passed to `Style` will be passed through.\n\n```js\nconst rulesAsFunction = ({ font }) =\u003e ({\n  body: {\n    fontFamily: valueAsFunction\n  }\n});\nconst valueAsFunction = ({ font }) =\u003e font;\n\n\u003cStyle font={\"Helvetica\"}\u003e{rulesAsFunction}\u003c/Style\u003e;\n```\n\nYou may also specify an `Array` for a value and it will be mapped into a string\nusing the standard rules listed above.\n\n```js\n\u003cStyle prop={\"property\"}\u003e\n  {{\n    body: {\n      margin: [10, 0]\n    }\n  }}\n\u003c/Style\u003e\n```\n\n_NOTE: All numeric values will get converted to `px` units._\n\n#### Passing props vs CSS variables\n\nProps are useful for passing in data from your application state. However, it's\nrecommended you simply use CSS variables where you don't need to do that.\n\n```js\n\u003cStyle\u003e\n  {{\n    \":root\": {\n      \"--grid-size\": 5\n    },\n    body: {\n      margin: [\"calc(var(--grid-size) * 2)\", 0]\n    }\n  }}\n\u003c/Style\u003e\n```\n\n#### Why can't you pass a string to `\u003cStyle /\u003e`?\n\nThere's currently no support for using strings as we'd have to parse the strings\nto simulate scoping and this is not simple nor inexpensive. Using objects makes\nit far simpler. We're not opposed to finding a way to be able to use strings,\nit's just not an immediate priority. Please reach out if this is something you'd\nlike to discuss.\n\n### `styled` - creating styled components\n\nThere is a `styled` export that is a shortcut for creating primitive components\nthat have a default styling.\n\n```js\nimport React from \"react\";\nimport { styled } from \"react-shade\";\n\nconst Div = styled(\"div\", {\n  \":host\": {\n    fontSize: \"1.2em\",\n    padding: 10\n  }\n});\n```\n\n#### Why don't you provide `styled.div()`?\n\nWe don't provide functions for each HTMLElement because it would cause a lot of\nbloat for little benefit. If you want to do this, it's pretty easy.\n\n```js\nconst div = styled.bind(null, \"div\");\nconst Div = div(css);\n```\n\n#### Why don't you provide `` styled.div`${css}` ``?\n\nWe don't provide a template literal API because we don't have to parse any CSS.\nYou can use `\u003cstyle /\u003e` directly, and use your own template literals, but we\ndon't provide scoping for it. To keep things simple, we've only provided scoping\nsimulation when running on the server (for SSR) or if using the Shadow DOM\npolyfill. For more information, see those sections.\n\n## Server-side rendering\n\nA big caveat of Shadow DOM for some is that it only comes with an imperative DOM\nAPI. This means that it doesn't support server-side rendering out of the box.\nWe're happy to say that react-shade supports server-side rendering and there's\nnothing you need to do on your end to make it work; it's 100% plug and play.\n\n\u003e \"The best API is no API\" - not me\n\nSimulated scoping currently supports:\n\n- `:host`\n- `:host(selector)`\n- `:host-context(selector)`\n- `any-selector` (will be prefixed by the scope and turned into a descendant\n  selector)\n\n**_Prefixing selectors is how other CSS-in-JS libraries simulate scoping._**\nIt's worth noting as a caveat because native Shadow DOM does not have this\nlimitation and provides much stronger selector scoping. The recommended thing to\ndo here would be to limit your use of descendant selectors within your\ncomponents.\n\nThe worst-case-scenario is that you might have a selector that bleeds in SSR or\nunder the polyfill. If your components are rehydrated in a browser that supports\nnative Shadow DOM, scoping will be fixed when the shadow roots are created.\n\n## Differences to native Shadow DOM\n\nA keen eye might spot some of these and notice that it's not how you'd normally\ndo Shadow DOM with imperative JavaScript or HTML. I assure you, that is only\nsuperficial. _All aspects of react-shade's API fully utilises the native APIs._\n\n## Using the Shadow DOM polyfill\n\nTo use `react-shade` in browsers that don't support the native APIs you'll want\nto include the Shadow DOM polyfill. You can find this at\nhttps://unpkg.com/@webcomponents/webcomponentsjs.\n\nThat polyfill doesn't support CSS scoping and integrating\n[`shadycss`](https://github.com/webcomponents/shadycss) is non-trivial, so we've\ngone ahead and simulated scoping when running under the polyfill because this is\nbasically the same thing as when running in an SSR environment.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftreshugart%2Freact-shade","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftreshugart%2Freact-shade","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftreshugart%2Freact-shade/lists"}