{"id":19094846,"url":"https://github.com/widgetti/ipyreact","last_synced_at":"2025-05-16T14:07:45.668Z","repository":{"id":153848618,"uuid":"630612789","full_name":"widgetti/ipyreact","owner":"widgetti","description":"React for ipywidgets that just works. No webpack, no npm, no hassle","archived":false,"fork":false,"pushed_at":"2025-01-28T12:37:05.000Z","size":1688,"stargazers_count":126,"open_issues_count":24,"forks_count":8,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-05-15T17:57:42.533Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/widgetti.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-04-20T18:59:14.000Z","updated_at":"2025-04-17T08:50:28.000Z","dependencies_parsed_at":"2024-02-19T15:49:08.272Z","dependency_job_id":"81152369-faa4-4e99-b5b9-caa99fea313d","html_url":"https://github.com/widgetti/ipyreact","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/widgetti%2Fipyreact","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/widgetti%2Fipyreact/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/widgetti%2Fipyreact/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/widgetti%2Fipyreact/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/widgetti","download_url":"https://codeload.github.com/widgetti/ipyreact/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254544146,"owners_count":22088807,"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-11-09T03:31:47.074Z","updated_at":"2025-05-16T14:07:45.616Z","avatar_url":"https://github.com/widgetti.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ipyreact\n\nReact for ipywidgets that just works. No webpack, no npm, no hassle. Just write jsx, tsx and python.\n\nBuild on top of [AnyWidget](https://anywidget.dev/).\n\n## Why\n\nIpyreact adds composability, allowing you to add children to your widget, which will render the whole react tree in\na single react context, without adding extra divs or creating a new react context.\n\nThis allows wrapping libraries such as [Material UI](https://mui.com/), [Ant Design](https://ant.design/) and even\n[React-three-fiber](https://docs.pmnd.rs/react-three-fiber/getting-started/introduction).\n\n## Tutorial\n\nThis tutorial will walk you through the steps of building a complete ipywidget with react.\n\n[![JupyterLight](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://widgetti.github.io/ipyreact/lab/?path=full_tutorial.ipynb)\n[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/widgetti/ipyreact/HEAD?labpath=examples%2Ffull_tutorial.ipynb)\n\nJust click the JupyterLite or Binder link to start the interactive walkthrough.\n\n## Goals\n\n- Take any [Material UI example](https://mui.com/material-ui/react-rating/), copy/paste the code, and it should work in Jupyter Notebook, Jupyter Lab, Voila, and more specifically, [Solara](https://github.com/widgetti/solara).\n- Wrap a library such as [Ant Design](https://ant.design/) giving the options to customize any JSON\u003c-\u003eJavaScript Object (de)serialization, such as the [DatePicker](https://ant.design/components/date-picker) which uses a dayjs object internally, which cannot be serialized over the wire to Python.\n- Compose widgets together to form a single react tree, with the same react context (e.g. useContext).\n\n## Examples\n\n### Inline code\n\n```python\nimport ipyreact\n\n\nclass ConfettiWidget(ipyreact.ValueWidget):\n    _esm = \"\"\"\n    import confetti from \"canvas-confetti\";\n    import * as React from \"react\";\n\n    export default function({value, setValue}) {\n        return \u003cbutton onClick={() =\u003e confetti() \u0026\u0026 setValue(value + 1)}\u003e\n            {value || 0} times confetti\n        \u003c/button\u003e\n    };\"\"\"\nConfettiWidget()\n```\n\n![initial-30-fps-compressed](https://user-images.githubusercontent.com/1765949/233469170-c659b670-07f5-4666-a201-80dea01ebabe.gif)\n\n(_NOTE: in the recording we used on_value, we now use setValue_)\n\n### Hot reloading\n\nCreate a tsx file:\n\n```tsx\n// confetti.tsx\nimport confetti from \"canvas-confetti\";\nimport * as React from \"react\";\n\nexport default function ({ value, setValue }) {\n  return (\n    \u003cbutton onClick={() =\u003e confetti() \u0026\u0026 setValue(value + 1)}\u003e\n      {value || 0} times confetti\n    \u003c/button\u003e\n  );\n}\n```\n\nAnd use it in your python code:\n\n```python\nimport ipyreact\nimport pathlib\n\n\nclass ConfettiWidget(ipyreact.ValueWidget):\n    _esm = pathlib.Path(\"confetti.tsx\")\n\nConfettiWidget()\n```\n\nNow edit, save, and see the changes in your browser/notebook.\n\n![hot-reload-compressed](https://user-images.githubusercontent.com/1765949/233470113-b2aa9284-71b9-44f0-bd52-906a08b06e14.gif)\n\n(_NOTE: in the recording we used on_value, we now use setValue_)\n\n### IPython magic\n\nFirst load the ipyreact extension:\n\n```python\n%load_ext ipyreact\n```\n\nThen use the `%%react` magic to directly write jsx/tsx in your notebook:\n\n```tsx\n%%react\nimport confetti from \"canvas-confetti\";\nimport * as React from \"react\";\n\nexport default function({value, setValue}) {\n    return \u003cbutton onClick={() =\u003e confetti() \u0026\u0026 setValue(value + 1)}\u003e\n        {value || 0} times confetti\n    \u003c/button\u003e\n};\n```\n\nAccess the underlying widget with the name `_last_react_widget` (e.g. `_last_react_widget.value` contains the number of clicks):\n\n![magic-optimized](https://user-images.githubusercontent.com/1765949/233471041-62e807d6-c16d-4fc5-af5d-13c0acb2c677.gif)\n\n(_NOTE: in the recording we used on_value, we now use setValue_)\n\n## Installation\n\nYou can install using `pip`:\n\n```bash\npip install ipyreact\n```\n\n## Usage\n\n### Summary\n\n- The `ValueWidget` has an `value` trait, which is a `traitlets.Any` trait. Use this to pass data to your react component, or to get data back from your react component (since it inherits from ipywidgets.ValueWidget it\n  can be used in combination with ipywidgets' [interact](https://ipywidgets.readthedocs.io/en/latest/examples/Using%20Interact.html)).\n- The `ipyreact.Widget` does not have the `value` trait.\n- All traits are added as props to your react component (e.g. `{value, setValue...}` pairs in the example above.\n- For every trait `ipyreact` automatically provides a `set\u003cTraitname\u003e` callback, which you can use to set the trait value from your react component (e.g. `setValue` in the example above). (_Note: we used `on_value` before, this is now deprecated_)\n- Props can de passed as `Widget(props={\"title\": \"My title\"})`, and contrary to a trait, will not add a `setTitle` callable to the props.\n- Children can be passed using `Widget(children=['text', or_widget])` supporting text, widgets, and un-interrupted rendering of ipyreact widgets.\n- Your code gets transpiled using [sucrase](https://github.com/alangpierce/sucrase) in the frontend, no bundler needed.\n- Your code should be written in ES modules.\n- Set `_debug=True` to get more debug information in the browser console.\n- Make sure you export a default function from your module (e.g. `export default function MyComponent() { ... }`). This is the component that will be rendered.\n- Pass `events={\"onClick\": handler}` to the constructor or add a method with the name `event_onClick(self, data=None)` to add a `onClick` callback to your props.\n\n### HTML elements\n\nYou do not need to provide the module code to create built-in HTML elements, ipyreact supports the same API as [React's createElement](https://react.dev/reference/react/createElement)\nallowing creation of buttons for instance.\n\n```python\nimport ipyreact\nipyreact.Widget(_type=\"button\", children=[\"click me\"])\n```\n\nNote that in addition to all native browser elements, also web components are supported.\n\n### Children\n\nAs shown in the above example, we also support children, which supports a list of strings (text), `ipyreact.Widget` widgets that will be rendered as an uninterrupted react tree, or\nany other `ipywidgets`\n\n```python\nimport ipyreact\nimport ipywidgets as widgets\nipyreact.Widget(_type=\"div\", children=[\n    \"normal text\",\n    ipyreact.Widget(_type=\"button\", children=[\"nested react widgets\"]),\n    widgets.FloatSlider(description=\"regular ipywidgets\")\n])\n```\n\n[![JupyterLight](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://widgetti.github.io/ipyreact/lab/?path=children.ipynb)\n[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/widgetti/ipyreact/HEAD?labpath=examples%2Fchildren.ipynb)\n\n### Events\n\nEvents can be passed via the event argument. In this case `onClick` will be added as a prop to the button element.\n\n```python\nimport ipyreact\nipyreact.Widget(_type=\"button\", children=[\"click me\"], events={\"onClick\": print})\n```\n\nSubclasses can also add an `event_onClick` method, which will also add a `onClick` event handler to the props.\n\n[![JupyterLight](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://widgetti.github.io/ipyreact/lab/?path=events.ipynb)\n[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/widgetti/ipyreact/HEAD?labpath=examples%2Fevents.ipynb)\n\n### Importing external modules\n\nWriting JSX code without having to compile/bundle is great, but so is using external libraries.\n\nIpyreact uses ES modules, which allows native importing of external libraries when written as an ES module.\nIn the example below, we use https://esm.sh/ which exposes many JS libraries as ES modules that\nwe can directly import.\n\n```python\nimport ipyreact\n\nipyreact.ValueWidget(\n    _esm=\"\"\"\n    import confetti from \"https://esm.sh/canvas-confetti@1.6.0\";\n    import * as React from \"react\";\n\n    export default function({value, setValue}) {\n        return \u003cbutton onClick={() =\u003e confetti() \u0026\u0026 setValue(value + 1)}\u003e\n            {value || 0} times confetti\n        \u003c/button\u003e\n    };\n    \"\"\"\n)\n```\n\n### Import maps\n\nHowever, the above code now has a direct link to \"https://esm.sh/canvas-confetti@1.6.0\" which makes the code very specific to esm.sh.\n\nTo address this, we also support [import maps](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap) to\nwrite code more independant of where the modules come from.\nYou can provide an import map using `ipyreact.define_import_map`, which takes a dictionary of module names to urls or other modules. By default we support `react` and `react-dom` which is prebundled.\n\nApart from `react`, the default we provide is:\n\n```python\ndefine_import_map({\n    \"@mui/material\": \"https://esm.sh/@mui/material@5.11.10?external=react,react-dom\",\n    \"@mui/material/\": \"https://esm.sh/@mui/material@5.11.10\u0026external=react,react-dom/\",\n    \"@mui/icons-material/\": \"https://esm.sh/@mui/icons-material/?external=react,react-dom\",\n    \"canvas-confetti\": \"https://esm.sh/canvas-confetti@1.6.0\",\n})\n```\n\n_Note that it is important to add `external=react,react-dom` for ReactJS based libraries, otherwise [esm.sh](https://esm.sh/#using-import-maps) would import ReactJS again_.\n\nWhich means we can now write our ConfettiButton as:\n\n```python\nimport ipyreact\n\n# note that this import_map is already part of the default\nipyreact.define_import_map({\n    \"canvas-confetti\": \"https://esm.sh/canvas-confetti@1.6.0\",\n})\n\n\nipyreact.ValueWidget(\n    _esm=\"\"\"\n    import confetti from \"canvas-confetti\";\n    import * as React from \"react\";\n\n    export default function({value, setValue}) {\n        return \u003cbutton onClick={() =\u003e confetti() \u0026\u0026 setValue(value + 1)}\u003e\n            {value || 0} times confetti\n        \u003c/button\u003e\n    };\n    \"\"\"\n)\n```\n\nAnd it also means we can copy paste _most_ of the examples from [mui](https://mui.com/)\n\n```tsx\n%%react -n my_widget -d\nimport {Button} from \"@mui/material\";\nimport confetti from \"canvas-confetti\";\nimport * as React from \"react\";\n\nexport default function({ value, setValue}) {\n  console.log(\"value=\", value);\n  return (\n    \u003cButton\n      variant=\"contained\"\n      onClick={() =\u003e confetti() \u0026\u0026 setValue(value + 1)}\n    \u003e\n      {value || 0} times confetti\n    \u003c/Button\u003e\n  );\n}\n```\n\nWe use the https://github.com/guybedford/es-module-shims shim to the browser page for the import maps functionality.\nThis also means that although import maps can be configured per widget, they configuration of import maps is global.\n\n### Bundled ESM modules\n\n## Creating the ES module\n\nWhile esm.sh is convenient to use, for production use, we recommend creating a standalone bundle. This will load faster and will not require a direct connection to esm.sh, which might not be available in airgapped or firewalled environments.\n\nWe will not create a minimal bundle for https://ant.design/\n\nFirst create a simple file called `antd-minimal.js` that exports what we need.\n\n```javascript\nexport { Button, Flex, Slider } from \"antd\";\n```\n\nNext, we install the libraries:\n\n```bash\n$ npm install antd\n```\n\nAnd use ESBuild to turn this into a self-contained module/bundle, without react, since ipyreact provides that for us.\n\n```\n$ npx esbuild ./antd-minimal.js --bundle --outfile=./antd-minimal.esm.js --format=esm --external:react --external:react-dom --target=esnext\n```\n\nNow we can define the module with a custom name (we call it antd-minimal).\n\n```python\nimport ipyreact\nfrom pathlib import Path\n\nipyreact.define_module(\"antd-minimal\", Path(\"./antd-minimal.esm.js\"))\n```\n\nWe can now use the components from this module:\n\n```python\ndef on_click(event_data):\n    w.children = [\"Clicked\"]\n\nw = ipyreact.Widget(_module=\"antd-minimal\", _type=\"Button\", children=[\"Hi there\"], events={\"onClick\": on_click})\nw\n```\n\nOr, composing multiple ones:\n\n```python\nstack = ipyreact.Widget(_module=\"antd-minimal\", _type=\"Flex\",\n    props={\"vertical\": True, \"style\": {\"padding\": \"24px\"}},\n    children=[\n        ipyreact.Widget(_module=\"antd-minimal\", _type=\"Button\", children=[\"Ant Design Button\"]),\n        ipyreact.Widget(_module=\"antd-minimal\", _type=\"Slider\",\n                       props={\"defaultValue\": 3, \"min\": 0, \"max\": 11}),\n])\nstack\n```\n\nInput components might need a little bit of custom code, and subclassing `ValueWidget`. It often means binding the value to the right prop of the input component (in this case the Slider takes the same name, `value`) and coupling the event handler (in this case `onChange`) to the `setValue` function.\n\n```python\nimport traitlets\n\n\nclass Slider(ipyreact.ValueWidget):\n    _esm = \"\"\"\n    import * as React from \"react\";\n    import {Slider} from \"antd-minimal\"\n\n    export default ({value, setValue, ...rest}) =\u003e {\n        return \u003cSlider value={value} onChange={(v) =\u003e setValue(v)} {...rest}/\u003e\n    }\n\n    \"\"\"\ns = Slider(value=2)\ns\n```\n\n_Note that it depends on the implementation of the event handler if the value is being passed directly, or a (synthetic) event with the data will be passed as argument. An typical example event handler could be `onChange={(event) =\u003e setValue(event.target.value)}`._\n\nNow the slider widget is stateful, and we have bi-directional communication using the `.value` trait.\nFor instance, we can read it:\n\n```python\ns.value\n```\n\nOr write to it, and it will be reflected directly in the UI.\n\n```python\ns.value = 10\n```\n\nTest this out in the notebook:\n[![JupyterLight](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://widgetti.github.io/ipyreact/lab/?path=antd/antd.ipynb)\n[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/widgetti/ipyreact/HEAD?labpath=examples%2Fantd%2Fantd.ipynb)\n\n### Bundled ES modules for threejs\n\nSee this notebook for a 3D WebGL threejs-fiber example\n\n[![JupyterLight](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://widgetti.github.io/ipyreact/lab/?path=threejs-fiber/threejs-fiber.ipynb)\n[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/widgetti/ipyreact/HEAD?labpath=examples%2Fthreejs-fiber%2Fthreejs-fiber.ipynb)\n\n## Development Installation\n\nCreate a dev environment:\n\n```bash\nconda create -n ipyreact-dev -c conda-forge nodejs yarn python 'jupyterlab\u003c4'\nconda activate ipyreact-dev\n```\n\nInstall the python. This will also build the TS package.\n\n```bash\npip install -e \".[test, examples, dev]\"\npre-commit install\n```\n\nWhen developing your extensions, you need to manually enable your extensions with the\nnotebook / lab frontend. For lab, this is done by the command:\n\n```\njupyter labextension develop --overwrite .\nyarn run build\n```\n\nFor classic notebook, you need to run:\n\n```\njupyter nbextension install --sys-prefix --symlink --overwrite --py ipyreact\njupyter nbextension enable --sys-prefix --py ipyreact\n```\n\nNote that the `--symlink` flag doesn't work on Windows, so you will here have to run\nthe `install` command every time that you rebuild your extension. For certain installations\nyou might also need another flag instead of `--sys-prefix`, but we won't cover the meaning\nof those flags here.\n\n## Binary data transport\n\nBinary data such as NumPy arrays, or Arrow data can be efficiently transported to the frontend.\nProps support object that support the buffer interface. See [this test as an example](https://github.com/widgetti/ipyreact/tree/master/tests/ui/serialize_test.py).\n\n### How to see your changes\n\n#### Typescript:\n\nIf you use JupyterLab to develop then you can watch the source directory and run JupyterLab at the same time in different\nterminals to watch for changes in the extension's source and automatically rebuild the widget.\n\n```bash\n# Watch the source directory in one terminal, automatically rebuilding when needed\nyarn run watch\n# Run JupyterLab in another terminal\njupyter lab\n```\n\nAfter a change wait for the build to finish and then refresh your browser and the changes should take effect.\n\n#### Python:\n\nIf you make a change to the python code then you will need to restart the notebook kernel to have it take effect.\n\n# FAQ\n\n## Which version of React do you use.\n\nWe currently only support React 18. Although we have some scaffolding in place to support different version, we do not have funding to support both.\n\n## Why does ipyreact provides React?\n\nIf several ReactJS components need to be composed into a single React app, they need to share the same React context. This makes it possible\nfor features such as React's [useContext](https://react.dev/reference/react/useContext) to work across the whole React tree.\nIf every library brings its own React, they cannot communicate using this. Also, every child would need to be nested in its own `\u003cdiv\u003e`\nwhich can affect the layout of your application. When ipyreact provides React, we can build a true ReactJS application with a normal/true\nReact render tree.\n\n## I get a React error\n\nFor instance, if you see `\"Cannot read properties of null (reading 'useReducer')\"` it means that you are loading in your own ReactJS version.\n\nIf you use https://esh.sh, make sure you add `??external=react,react-dom` at the end of the url, so that your esm bundle doesn't include its own\nReactJS version, but uses the one provided with ipyreact.\n\nIf you make your own bundle using esbuild, make sure to add the `--external:react --external:react-dom` flags on the CLI.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwidgetti%2Fipyreact","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwidgetti%2Fipyreact","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwidgetti%2Fipyreact/lists"}