Minimal Vite React example #287
-
I've just made an experiment to see if I can run a minimal react app with Anywidget+Vite export default function App() {
return <h1>Hello Anywidget + Vite!</h1>;
} but I get this error: ![]() Steps to reproduceI've followed the official vite tutorial and the tutorial from the anywidget docs in order to set up a sample project at https://github.com/Octoframes/anywidget-react-vite-test. The setup process ran without any errors, only displaying the widget does not work.
// vite.config.js
import { defineConfig } from "vite";
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
build: {
outDir: "hello_widget/static",
lib: {
entry: ["src/main.jsx"],
formats: ["es"],
},
},
});
anywidget-react-vite-test/
├── pyproject.toml
├── hello_widget/
│ └── __init__.py
│ └── static/
+ └── main.js
└── hello.ipynb
If you currently have some bandwidth, I'd be curious to hear your thoughts on this @manzt @maartenbreddels ✨ |
Beta Was this translation helpful? Give feedback.
Replies: 25 comments
-
Add this to your vite config: define: {
'process.env.NODE_ENV': '"production"'
}, The issue is that if (process.env.NODE_ENV !== "production") {
} to if ("production" !== "production") {
} which bundlers can statically analyze and dead-code eliminate. |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
and one further observation: running
|
Beta Was this translation helpful? Give feedback.
-
hmm, this could be done to the new automatic runtime in defaults in |
Beta Was this translation helpful? Give feedback.
-
Thanks for the suggestion, nope, that did not work. |
Beta Was this translation helpful? Give feedback.
-
with the classic runtime you must |
Beta Was this translation helpful? Give feedback.
-
Ok, I got it sorted out. The issue is that you are bundling react in the library mode in vite. You need the // vite.config.js
import { defineConfig } from "vite";
import react from '@vitejs/plugin-react'
export default defineConfig(({ command }) => {
let define = {};
if (command === "build") {
define["process.env.NODE_ENV"] = JSON.stringify("production");
}
return {
plugins: [react()],
build: {
outDir: "hello_widget/static",
lib: {
entry: ["src/main.jsx"],
formats: ["es"],
},
},
define,
}
}); |
Beta Was this translation helpful? Give feedback.
-
Nice, now I have import * as React from "react";
export default function App() {
return <h1>Hello Anywidget + Vite!</h1>;
} and // vite.config.js
import { defineConfig } from "vite";
import react from '@vitejs/plugin-react'
export default defineConfig(async ({ command }) => {
let define = {};
if (command === "build") {
define["process.env.NODE_ENV"] = JSON.stringify("production");
}
return {
plugins: [react()],
build: {
outDir: "hello_widget/static",
lib: {
entry: ["src/main.jsx"],
formats: ["es"],
},
},
define,
}
}); this way the website works now again. What remains is the
|
Beta Was this translation helpful? Give feedback.
-
This makes sense. Your If you want to make a react app with Vite, I'd recommend following the docs and using the |
Beta Was this translation helpful? Give feedback.
-
If you want to develop separately, I'd recommend creating a // widget.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
export function render({ model, el }) {
let root = ReactDOM.createRoot(el);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
return () => root.unmount();
} and update your build with: // vite.config.js
import { defineConfig } from "vite";
import react from '@vitejs/plugin-react'
export default defineConfig(async ({ command }) => {
let define = {};
if (command === "build") {
define["process.env.NODE_ENV"] = JSON.stringify("production");
}
return {
plugins: [react()],
build: {
outDir: "hello_widget/static",
lib: {
++ entry: ["src/widget.jsx"],
formats: ["es"],
},
},
define,
}
}); |
Beta Was this translation helpful? Give feedback.
-
Nice, I will try that out and will let you know soon if I was able to get it working! :) |
Beta Was this translation helpful? Give feedback.
-
![]() ok, I've now added and installed the vite plugin from https://anywidget.dev/en/bundling/#development-1 and tried to run Getting this minimal example working with vite would be really awesome! |
Beta Was this translation helpful? Give feedback.
-
ohhh. |
Beta Was this translation helpful? Give feedback.
-
and interactive mafs components render as well! 🎉 Screen.Recording.2023-07-17.at.19.56.51.mov |
Beta Was this translation helpful? Give feedback.
-
This is how I see the next step up. First, play around with ipyreact, nothing needed except a browser. If you want to package this, or deploy this in production, you probably want to build a bundle using a modern toolchain ala vite or esbuild. I haven't tried this myself yet, so thanks for showing the pitfalls :) |
Beta Was this translation helpful? Give feedback.
-
It's a pleasure to be on this discovery journey with the two of you! ⛵ The last step, shipping to pypi via poetry, was almost TOO easy 🏝️🤩 I still have some remaining questions. For these questions, I would then create separate issues. |
Beta Was this translation helpful? Give feedback.
-
Just stumbled about the first question: but when I change -- return <QRCode value="Hiiii" />
++ return <QRCode value={content} /> then the output is just white, without the rendered content. |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
You need to connect your "app" to the Jupyter Widget model. I've tried to detail the important bits of connecting JS with Python in the docs. But, most simply: // widget.jsx
+ <App content={model.get("content")} /> This code grabs the initial value of To perhaps anticipate your next question, "How do you update or re-render the component when From the anywidget docs:
The code in To accomplish 2, you'll need to wire up some React hooks around the // widget.jsx
// Connects React App component to the Backbone model
function WidgetAdapter({ model }) {
let [content, setContent] = React.useState(model.get("content"));
React.useEffect(() => {
// update content anytime the model changes (from either Python or JS)
model.on("change:content", () => setContent(model.get("content")));
}, [])
return <App content={content} />
}
export function render({ model, el }) {
let root = ReactDOM.createRoot(el);
root.render(
<React.StrictMode>
<WidgetAdapter model={model} />
</React.StrictMode>,
);
return () => root.unmount();
} |
Beta Was this translation helpful? Give feedback.
-
This would be great! I've also been meaning to record some videos. Maybe we could chat sometime about how to improve the anywidget docs / create more resources.
Sure, I'll have a look. |
Beta Was this translation helpful? Give feedback.
-
awesome, that works! 🎉 😊
when I first saw anywidget I was already suspicious that you're a magician, but now it's confirmed! 🧙 just incorporated that hook for updating from the traitlet as well, and also works out of the box 🎉
I'm absolutely up for that, looking forward to connecting with you!
Amazing, thank you so much for your time and support, I really appreciate that. One remaining polishing challenge: This minimal qr-code example currently lives at https://github.com/Octoframes/anywidget-react-vite-test, feel free to add commits directly to main there. |
Beta Was this translation helpful? Give feedback.
-
I'm going to close this issue, in favor of #190 and our on-going discussion in anywidget-react-vite-test. |
Beta Was this translation helpful? Give feedback.
-
Sounds good. I am at a conference / traveling the next couple of weeks but will reach out to find a time to chat. |
Beta Was this translation helpful? Give feedback.
-
The recommended approach for creating a React widget now is to use our CLI (and select React option!): npm create anywidget@latest |
Beta Was this translation helpful? Give feedback.
The recommended approach for creating a React widget now is to use our CLI (and select React option!):