diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/LineComponent.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/LineComponent.tsx
new file mode 100644
index 0000000000..86785a4219
--- /dev/null
+++ b/invokeai/frontend/web/src/features/regionalPrompts/components/LineComponent.tsx
@@ -0,0 +1,23 @@
+import { rgbaColorToString } from 'features/canvas/util/colorToString';
+import { useTransform } from 'features/regionalPrompts/hooks/useTransform';
+import type { LineObject } from 'features/regionalPrompts/store/regionalPromptsSlice';
+import { Line } from 'react-konva';
+
+type Props = {
+ line: LineObject;
+};
+
+export const LineComponent = ({ line }: Props) => {
+ const { shapeRef } = useTransform(line);
+
+ return (
+
+ );
+};
diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/RectComponent.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/RectComponent.tsx
new file mode 100644
index 0000000000..e2b83a47e7
--- /dev/null
+++ b/invokeai/frontend/web/src/features/regionalPrompts/components/RectComponent.tsx
@@ -0,0 +1,25 @@
+import { rgbaColorToString } from 'features/canvas/util/colorToString';
+import { useTransform } from 'features/regionalPrompts/hooks/useTransform';
+import type { FillRectObject } from 'features/regionalPrompts/store/regionalPromptsSlice';
+import { Rect } from 'react-konva';
+
+type Props = {
+ rect: FillRectObject;
+};
+
+export const RectComponent = ({ rect }: Props) => {
+ const { shapeRef } = useTransform(rect);
+
+ return (
+
+ );
+};
diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptingEditor.stories.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptingEditor.stories.tsx
new file mode 100644
index 0000000000..a67e9f58f7
--- /dev/null
+++ b/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptingEditor.stories.tsx
@@ -0,0 +1,19 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { RegionalPromptsEditor } from 'features/regionalPrompts/components/RegionalPromptsEditor';
+
+const meta: Meta = {
+ title: 'Feature/RegionalPrompts',
+ tags: ['autodocs'],
+ component: RegionalPromptsEditor,
+};
+
+export default meta;
+type Story = StoryObj;
+
+const Component = () => {
+ return ;
+};
+
+export const Default: Story = {
+ render: Component,
+};
diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsEditor.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsEditor.tsx
new file mode 100644
index 0000000000..907f8a2c69
--- /dev/null
+++ b/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsEditor.tsx
@@ -0,0 +1,25 @@
+import { Flex } from '@invoke-ai/ui-library';
+import { createSelector } from '@reduxjs/toolkit';
+import { useAppSelector } from 'app/store/storeHooks';
+import { RegionalPromptsStage } from 'features/regionalPrompts/components/RegionalPromptsStage';
+import { layersSelectors, selectRegionalPromptsSlice } from 'features/regionalPrompts/store/regionalPromptsSlice';
+
+const selectLayers = createSelector(selectRegionalPromptsSlice, (regionalPrompts) =>
+ layersSelectors.selectAll(regionalPrompts)
+);
+
+export const RegionalPromptsEditor = () => {
+ const layers = useAppSelector(selectLayers);
+ return (
+
+
+ {layers.map((layer) => (
+ {layer.prompt}
+ ))}
+
+
+
+
+
+ );
+};
diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsStage.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsStage.tsx
new file mode 100644
index 0000000000..b80d142a84
--- /dev/null
+++ b/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsStage.tsx
@@ -0,0 +1,39 @@
+import { createSelector } from '@reduxjs/toolkit';
+import { useAppSelector } from 'app/store/storeHooks';
+import { LineComponent } from 'features/regionalPrompts/components/LineComponent';
+import { RectComponent } from 'features/regionalPrompts/components/RectComponent';
+import {
+ layerObjectsSelectors,
+ layersSelectors,
+ selectRegionalPromptsSlice,
+} from 'features/regionalPrompts/store/regionalPromptsSlice';
+import { memo } from 'react';
+import { Group, Layer, Stage } from 'react-konva';
+
+const selectLayers = createSelector(selectRegionalPromptsSlice, (regionalPrompts) =>
+ layersSelectors.selectAll(regionalPrompts)
+);
+
+export const RegionalPromptsStage: React.FC = memo(() => {
+ const layers = useAppSelector(selectLayers);
+ return (
+
+
+ {layers.map((layer) => (
+
+ {layerObjectsSelectors.selectAll(layer.objects).map((obj) => {
+ if (obj.kind === 'line') {
+ return ;
+ }
+ if (obj.kind === 'fillRect') {
+ return ;
+ }
+ })}
+
+ ))}
+
+
+ );
+});
+
+RegionalPromptsStage.displayName = 'RegionalPromptingEditor';
diff --git a/invokeai/frontend/web/src/features/regionalPrompts/hooks/useTransform.ts b/invokeai/frontend/web/src/features/regionalPrompts/hooks/useTransform.ts
new file mode 100644
index 0000000000..d04cbb8c55
--- /dev/null
+++ b/invokeai/frontend/web/src/features/regionalPrompts/hooks/useTransform.ts
@@ -0,0 +1,30 @@
+import type { FillRectObject, LayerObject, LineObject } from 'features/regionalPrompts/store/regionalPromptsSlice';
+import type { Image } from 'konva/lib/shapes/Image';
+import type { Line } from 'konva/lib/shapes/Line';
+import type { Rect } from 'konva/lib/shapes/Rect';
+import type { Transformer } from 'konva/lib/shapes/Transformer';
+import { useEffect, useRef } from 'react';
+
+type ShapeType = T extends LineObject ? Line : T extends FillRectObject ? Rect : Image;
+
+export const useTransform = (object: TObject) => {
+ const shapeRef = useRef>(null);
+ const transformerRef = useRef(null);
+
+ useEffect(() => {
+ if (!object.isSelected) {
+ return;
+ }
+
+ if (!transformerRef.current || !shapeRef.current) {
+ return;
+ }
+
+ if (object.isSelected) {
+ transformerRef.current.nodes([shapeRef.current]);
+ transformerRef.current.getLayer()?.batchDraw();
+ }
+ }, [object.isSelected]);
+
+ return { shapeRef, transformerRef };
+};