diff --git a/custom-nodes/js/javascript_overview.mdx b/custom-nodes/js/javascript_overview.mdx
index aaa561e4..65cf07f2 100644
--- a/custom-nodes/js/javascript_overview.mdx
+++ b/custom-nodes/js/javascript_overview.mdx
@@ -10,11 +10,13 @@ Comfy can be modified through an extensions mechanism. To add an extension you n
- Place one or more `.js` files into that directory,
- Use `app.registerExtension` to register your extension.
-These three steps are below. Once you know how to add an extension, look
-through the [hooks](/custom-nodes/js/javascript_hooks) available to get your code called,
+These three steps are below. Once you know how to add an extension, look
+through the [hooks](/custom-nodes/js/javascript_hooks) available to get your code called,
a description of various [Comfy objects](/custom-nodes/js/javascript_objects_and_hijacking) you might need,
or jump straight to some [example code snippets](/custom-nodes/js/javascript_examples).
+For building custom node widgets with Vue components, see [Nodes 2.0 Widgets](/custom-nodes/js/vue_widgets).
+
### Exporting `WEB_DIRECTORY`
The Comfy web client can be extended by creating a subdirectory in your custom node directory, conventionally called `js`, and
diff --git a/custom-nodes/js/javascript_sidebar_tabs.mdx b/custom-nodes/js/javascript_sidebar_tabs.mdx
index a393df83..8b8fb06f 100644
--- a/custom-nodes/js/javascript_sidebar_tabs.mdx
+++ b/custom-nodes/js/javascript_sidebar_tabs.mdx
@@ -131,6 +131,8 @@ app.extensionManager.registerSidebarTab({
For a real-world example of a React application integrated as a sidebar tab, check out the [ComfyUI-Copilot project on GitHub](https://github.com/AIDC-AI/ComfyUI-Copilot).
+For creating custom node widgets with Vue components, see [Nodes 2.0 Widgets](/custom-nodes/js/vue_widgets).
+
## Dynamic Content Updates
You can update sidebar content in response to graph changes:
diff --git a/custom-nodes/js/vue_widgets.mdx b/custom-nodes/js/vue_widgets.mdx
new file mode 100644
index 00000000..490d9c5e
--- /dev/null
+++ b/custom-nodes/js/vue_widgets.mdx
@@ -0,0 +1,374 @@
+---
+title: "Nodes 2.0 Widgets"
+description: "Create custom node widgets using Vue Single File Components"
+---
+
+Nodes 2.0 widgets allow you to create rich, interactive node widgets using Vue 3 Single File Components (SFCs). This is the recommended approach for building custom widgets that need complex UI interactions, state management, or styling.
+
+## Overview
+
+You can create Vue-based widgets using the `getCustomVueWidgets()` hook. ComfyUI exposes Vue globally as `window.Vue`, so your extension uses the same Vue instance as the main app (smaller bundle size).
+
+## Project Structure
+
+```
+test_vue_widget_node/
+├── __init__.py # Python node definitions
+└── web/
+ ├── src/
+ │ ├── extension.js # Entry point - registers extension
+ │ ├── styles.css # Tailwind directives
+ │ └── WidgetStarRating.vue
+ ├── dist/
+ │ └── extension.js # Built output (loaded by ComfyUI)
+ ├── package.json
+ ├── vite.config.ts
+ ├── tailwind.config.js
+ └── postcss.config.js
+```
+
+## Complete Example
+
+### package.json
+
+```json
+{
+ "name": "test-vue-widget-node",
+ "version": "1.0.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "build": "vite build",
+ "dev": "vite build --watch"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-vue": "^5.0.0",
+ "autoprefixer": "^10.4.20",
+ "postcss": "^8.4.49",
+ "rollup-plugin-external-globals": "^0.13.0",
+ "tailwindcss": "^3.4.17",
+ "vite": "^6.0.0",
+ "vite-plugin-css-injected-by-js": "^3.5.2",
+ "vue": "^3.5.0"
+ }
+}
+```
+
+### vite.config.ts
+
+```typescript
+import vue from '@vitejs/plugin-vue'
+import externalGlobals from 'rollup-plugin-external-globals'
+import { defineConfig } from 'vite'
+import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
+
+export default defineConfig({
+ plugins: [vue(), cssInjectedByJsPlugin()],
+ build: {
+ lib: {
+ entry: 'src/extension.js',
+ name: 'TestVueWidgets',
+ fileName: () => 'extension.js',
+ formats: ['es']
+ },
+ outDir: 'dist',
+ emptyOutDir: true,
+ cssCodeSplit: false,
+ rollupOptions: {
+ external: ['vue', /^\.\.\/.*\.js$/],
+ plugins: [externalGlobals({ vue: 'Vue' })]
+ }
+ }
+})
+```
+
+Key configuration points:
+
+| Option | Purpose |
+|--------|---------|
+| `cssInjectedByJsPlugin()` | Inlines CSS into the JS bundle |
+| `external: ['vue', ...]` | Don't bundle Vue, use global |
+| `externalGlobals({ vue: 'Vue' })` | Map Vue imports to `window.Vue` |
+
+### extension.js
+
+```javascript
+/**
+ * Test Vue Widget Extension
+ *
+ * Demonstrates how to register custom Vue widgets for ComfyUI nodes.
+ * Widgets are built from .vue SFC files using Vite.
+ */
+
+import './styles.css'
+import { app } from '../../scripts/app.js'
+
+// Import Vue components
+import WidgetStarRating from './WidgetStarRating.vue'
+
+// Register the extension
+app.registerExtension({
+ name: 'TestVueWidgets',
+
+ getCustomVueWidgets() {
+ return {
+ star_rating: {
+ component: WidgetStarRating,
+ aliases: ['STAR_RATING']
+ }
+ }
+ }
+})
+```
+
+### WidgetStarRating.vue
+
+```vue
+
+
+
+ {{ widget.label ?? widget.name }}
+
+
+
+
+
+ {{ modelValue }}/{{ maxStars }}
+
+
+
+
+
+```
+
+### __init__.py
+
+```python
+"""
+Test Vue Widget Node
+
+A test custom node that demonstrates the Vue widget registration feature.
+This node uses a custom STAR_RATING widget type that is rendered by a Vue component.
+"""
+
+
+class TestVueWidgetNode:
+ """A test node with a custom Vue-rendered star rating widget."""
+
+ @classmethod
+ def INPUT_TYPES(cls):
+ return {
+ "required": {
+ "rating": ("INT", {
+ "default": 3,
+ "min": 0,
+ "max": 5,
+ "display": "star_rating", # Custom widget type hint
+ }),
+ "text_input": ("STRING", {
+ "default": "Hello Vue Widgets!",
+ "multiline": False,
+ }),
+ },
+ }
+
+ RETURN_TYPES = ("INT", "STRING")
+ RETURN_NAMES = ("rating_value", "text_value")
+ FUNCTION = "process"
+ CATEGORY = "Testing/Vue Widgets"
+ DESCRIPTION = "Test node for Vue widget registration feature"
+
+ def process(self, rating: int, text_input: str):
+ print(f"[TestVueWidgetNode] Rating: {rating}, Text: {text_input}")
+ return (rating, text_input)
+
+
+NODE_CLASS_MAPPINGS = {
+ "TestVueWidgetNode": TestVueWidgetNode,
+}
+
+NODE_DISPLAY_NAME_MAPPINGS = {
+ "TestVueWidgetNode": "Test Vue Widget (Star Rating)",
+}
+
+WEB_DIRECTORY = "./web/dist"
+__all__ = ['NODE_CLASS_MAPPINGS', 'WEB_DIRECTORY']
+```
+
+## Using Tailwind CSS
+
+### tailwind.config.js
+
+```javascript
+/** @type {import('tailwindcss').Config} */
+export default {
+ content: ['./src/**/*.{vue,js,ts}'],
+ corePlugins: {
+ preflight: false // Disable base reset to avoid affecting other parts of the app
+ },
+ theme: {
+ extend: {}
+ },
+ plugins: []
+}
+```
+
+Always set `preflight: false` to prevent Tailwind's CSS reset from affecting ComfyUI's styles.
+
+### postcss.config.js
+
+```javascript
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {}
+ }
+}
+```
+
+### styles.css
+
+```css
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+```
+
+### Handling Positioning
+
+Without Tailwind's preflight, some utility classes like `absolute`, `translate-x-1/2` may not work as expected. Use inline styles for critical positioning:
+
+```vue
+
+ Centered content
+
+```
+
+## Widget Component API
+
+Your Vue component receives these props:
+
+| Prop | Type | Description |
+|------|------|-------------|
+| `widget` | `Object` | Widget configuration object |
+| `widget.name` | `string` | Widget name from Python input |
+| `widget.label` | `string` | Display label |
+| `widget.options` | `Object` | Options from Python (`min`, `max`, `step`, etc.) |
+| `widget.options.disabled` | `boolean` | Whether the widget is disabled |
+
+Use `defineModel()` for two-way value binding:
+
+```vue
+
+```
+
+## Build and Test
+
+```bash
+cd web
+npm install
+npm run build
+```
+
+Restart ComfyUI to load your extension.
+
+## Development Workflow
+
+Run the watcher during development:
+
+```bash
+npm run dev
+```
+
+This rebuilds `dist/extension.js` on every file change. Refresh ComfyUI to see updates.
+
+## Common Pitfalls
+
+### Failed to resolve module specifier "vue"
+
+The browser can't resolve bare `"vue"` imports.
+
+**Solution:** Use `rollup-plugin-external-globals` to map Vue imports to `window.Vue`:
+
+```typescript
+import externalGlobals from 'rollup-plugin-external-globals'
+
+rollupOptions: {
+ external: ['vue', /^\.\.\/.*\.js$/],
+ plugins: [externalGlobals({ vue: 'Vue' })]
+}
+```
+
+### CSS Not Loading
+
+ComfyUI only loads JS files. CSS must be inlined into the bundle.
+
+**Solution:** Use `vite-plugin-css-injected-by-js`.
+
+### Styles Affecting Other UI
+
+Tailwind's preflight resets global styles, breaking ComfyUI.
+
+**Solution:** Set `preflight: false` in `tailwind.config.js`.
+
+### Accessing ComfyUI's App
+
+Import from the relative path to `scripts/app.js`:
+
+```javascript
+import { app } from '../../scripts/app.js'
+```
+
+Configure Vite to preserve this import:
+
+```typescript
+rollupOptions: {
+ external: [/^\.\.\/.*\.js$/]
+}
+```
+
+## Examples
+
+- [ComfyUI_vue_widget_example](https://github.com/Myestery/ComfyUI_vue_widget_example) - Minimal example with star rating widget (source for this documentation)
+- [ComfyUI_frontend_vue_basic](https://github.com/jtydhr88/ComfyUI_frontend_vue_basic) - Full Vue extension with PrimeVue, i18n, drawing board widget
diff --git a/custom-nodes/overview.mdx b/custom-nodes/overview.mdx
index 6f5d83b6..f4f6a7a0 100644
--- a/custom-nodes/overview.mdx
+++ b/custom-nodes/overview.mdx
@@ -11,10 +11,12 @@ simple node that takes an image and inverts it.

-Custom node examples:
-- [cookiecutter-comfy-extension](https://github.com/Comfy-Org/cookiecutter-comfy-extension)
-- [ComfyUI-React-Extension-Template](https://github.com/Comfy-Org/ComfyUI-React-Extension-Template)
-- [ComfyUI_frontend_vue_basic](https://github.com/jtydhr88/ComfyUI_frontend_vue_basic)
+Custom node examples:
+- [cookiecutter-comfy-extension](https://github.com/Comfy-Org/cookiecutter-comfy-extension) - Python-only template
+- [ComfyUI-React-Extension-Template](https://github.com/Comfy-Org/ComfyUI-React-Extension-Template) - React UI extension
+- [ComfyUI_frontend_vue_basic](https://github.com/jtydhr88/ComfyUI_frontend_vue_basic) - Vue UI extension
+
+For creating custom node widgets with Vue, see [Nodes 2.0 Widgets](/custom-nodes/js/vue_widgets).
## Client-Server Model
diff --git a/docs.json b/docs.json
index c8b78fc0..288e1f37 100644
--- a/docs.json
+++ b/docs.json
@@ -566,6 +566,7 @@
"custom-nodes/js/javascript_about_panel_badges",
"custom-nodes/js/javascript_bottom_panel_tabs",
"custom-nodes/js/javascript_sidebar_tabs",
+ "custom-nodes/js/vue_widgets",
"custom-nodes/js/javascript_selection_toolbox",
"custom-nodes/js/javascript_commands_keybindings",
"custom-nodes/js/javascript_topbar_menu",