diff --git a/.changeset/strange-kings-sit.md b/.changeset/strange-kings-sit.md
new file mode 100644
index 000000000..05f5419d9
--- /dev/null
+++ b/.changeset/strange-kings-sit.md
@@ -0,0 +1,5 @@
+---
+"@cube-dev/ui-kit": patch
+---
+
+Introduces a new render helper component ``. Now you can optimize rendering of intensive items like IDE tabs.
diff --git a/src/stories/RenderCache.docs.mdx b/src/stories/RenderCache.docs.mdx
new file mode 100644
index 000000000..e804648c3
--- /dev/null
+++ b/src/stories/RenderCache.docs.mdx
@@ -0,0 +1,181 @@
+import { Meta, Canvas, Story, Controls } from '@storybook/addon-docs/blocks';
+import * as RenderCacheStories from './RenderCache.stories';
+
+
+
+# RenderCache
+
+RenderCache is a performance optimization component that caches rendered React elements and only re-renders specific items when needed. It's useful for lists where most items remain unchanged but you need fine-grained control over which items to re-render.
+
+## When to Use
+
+- When rendering lists where only specific items need to re-render based on state changes
+- When child components are expensive to render and don't need to update on every parent re-render
+- When you want more control than React.memo provides, with explicit per-item cache invalidation
+- When building complex UI where selective re-rendering improves performance significantly
+
+## Component
+
+
+
+---
+
+### Properties
+
+
+
+### Property Details
+
+- **items**: Array of items to render
+- **renderKeys**: Array of keys that should trigger re-render. Only items with keys in this array will be re-rendered
+- **getKey**: Function that extracts a unique key from each item
+- **children**: Render function that takes an item and returns a React element
+
+### Base Properties
+
+This is a headless component and does not support base properties.
+
+## How It Works
+
+The component maintains an internal cache of rendered elements. When rendering:
+
+1. For each item, it checks if the item's key is in renderKeys
+2. If the key is in renderKeys or the item hasn't been rendered before, it calls the children function to render/re-render
+3. Otherwise, it returns the cached element from the previous render
+4. It automatically cleans up cache entries for items that are no longer in the list
+
+## Examples
+
+### Basic Usage
+
+```jsx
+import { RenderCache } from '@cube-dev/ui-kit';
+
+const items = [
+ { id: 1, name: 'Item 1' },
+ { id: 2, name: 'Item 2' },
+ { id: 3, name: 'Item 3' },
+];
+
+const [selectedId, setSelectedId] = useState(1);
+
+ item.id}
+>
+ {(item) => }
+
+```
+
+### With List of Expensive Components
+
+```jsx
+import { RenderCache } from '@cube-dev/ui-kit';
+
+function ExpensiveListItem({ item, isActive }) {
+ // Complex rendering logic
+ return
{item.name}
;
+}
+
+const [activeId, setActiveId] = useState(1);
+
+ item.id}
+>
+ {(item) => (
+
+ )}
+
+```
+
+## Performance Considerations
+
+- **Cache management**: The component uses useRef to maintain the cache across renders, avoiding unnecessary re-allocations
+- **Automatic cleanup**: Removes cached entries for items no longer in the list to prevent memory leaks
+- **Selective invalidation**: Only items with keys in renderKeys are re-rendered, others use cached elements
+- **Best for expensive renders**: Most effective when child components have significant render cost
+
+## Best Practices
+
+1. **Use stable keys**: Ensure getKey returns consistent keys for the same items across renders
+ ```jsx
+ // Good: Using stable IDs
+ item.id}
+ renderKeys={[selectedId]}
+ >
+ {(item) => }
+
+
+ // Bad: Using indices (unstable when list changes)
+ index}
+ renderKeys={[selectedIndex]}
+ >
+ {(item) => }
+
+ ```
+
+2. **Minimize renderKeys**: Only include keys that truly need re-rendering to maximize cache benefits
+ ```jsx
+ // Good: Only re-render the active item
+ item.id}
+ >
+ {(item) => }
+
+
+ // Bad: Re-rendering all items defeats the purpose
+ item.id)}
+ getKey={(item) => item.id}
+ >
+ {(item) => }
+
+ ```
+
+3. **Consider alternatives**: For simple cases, React.memo might be sufficient and simpler
+
+4. **Profile first**: Use React DevTools Profiler to confirm you have a performance issue before adding this optimization
+
+5. **Avoid inline functions**: Use stable render functions to prevent unnecessary cache invalidation
+ ```jsx
+ // Good: Stable render function
+ const renderItem = useCallback((item) => (
+
+ ), []);
+
+ item.id}
+ >
+ {renderItem}
+
+ ```
+
+## When NOT to Use
+
+- **Simple lists**: For simple lists without performance issues, regular rendering is simpler
+- **All items update frequently**: If all items need to re-render on every change, the cache overhead isn't worth it
+- **Small lists**: Lists with < 10 items typically don't benefit from caching
+- **Simple components**: If child components render quickly, the optimization overhead may outweigh benefits
+
+## Related Components
+
+- **React.memo** - For simple component memoization
+- **useMemo** - For memoizing computed values
+- **useCallback** - For memoizing callback functions
+- **DisplayTransition** - For animating component mount/unmount
+
diff --git a/src/stories/RenderCache.stories.tsx b/src/stories/RenderCache.stories.tsx
new file mode 100644
index 000000000..f728cb704
--- /dev/null
+++ b/src/stories/RenderCache.stories.tsx
@@ -0,0 +1,138 @@
+import { useRef, useState } from 'react';
+import { userEvent, within } from 'storybook/test';
+
+import { Block } from '../components/Block';
+import { Radio } from '../components/fields/RadioGroup/Radio';
+import { Flow } from '../components/layout/Flow';
+import { Space } from '../components/layout/Space';
+import { RenderCache } from '../utils/react/RenderCache';
+
+import type { Meta, StoryObj } from '@storybook/react-vite';
+
+const meta = {
+ title: 'Helpers/RenderCache',
+ component: RenderCache,
+ argTypes: {
+ items: {
+ control: { type: null },
+ description: 'Array of items to render',
+ },
+ renderKeys: {
+ control: { type: null },
+ description:
+ 'Array of keys that should trigger re-render. Only items with keys in this array will be re-rendered',
+ },
+ getKey: {
+ control: { type: null },
+ description: 'Function that extracts a unique key from each item',
+ },
+ children: {
+ control: { type: null },
+ description:
+ 'Render function that takes an item and returns a React element',
+ },
+ },
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+// Item component that tracks and displays render count
+function RenderCountItem({ id }: { id: number }) {
+ const renderCount = useRef(0);
+ renderCount.current += 1;
+
+ return (
+
+ Item {id}: Rendered {renderCount.current} time
+ {renderCount.current !== 1 ? 's' : ''}
+
+ );
+}
+
+export const Default: Story = {
+ render: () => {
+ const [selectedTab, setSelectedTab] = useState('1');
+ const items = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }];
+
+ return (
+
+
+ Item 1
+ Item 2
+ Item 3
+ Item 4
+ Item 5
+
+
+
+
+ item.id}
+ >
+ {(item) => }
+
+
+
+
+
+ How it works: Only the selected item re-renders when
+ you switch tabs. Other items show their cached render count. This
+ demonstrates how RenderCache optimizes performance by avoiding
+ unnecessary re-renders.
+
+
+ );
+ },
+ play: async ({ canvasElement }) => {
+ const canvas = within(canvasElement);
+
+ // Wait for initial render
+ await new Promise((resolve) => setTimeout(resolve, 100));
+
+ // Click on Item 2 tab
+ const item2Tab = canvas.getByRole('radio', { name: 'Item 2' });
+ await userEvent.click(item2Tab);
+
+ // Wait for render
+ await new Promise((resolve) => setTimeout(resolve, 100));
+
+ // Click on Item 3 tab
+ const item3Tab = canvas.getByRole('radio', { name: 'Item 3' });
+ await userEvent.click(item3Tab);
+
+ // Wait for render
+ await new Promise((resolve) => setTimeout(resolve, 100));
+
+ // Click on Item 5 tab
+ const item5Tab = canvas.getByRole('radio', { name: 'Item 5' });
+ await userEvent.click(item5Tab);
+
+ // Wait for render
+ await new Promise((resolve) => setTimeout(resolve, 100));
+
+ // Click back to Item 2 to show it re-renders again
+ await userEvent.click(item2Tab);
+
+ // Final wait
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ },
+};
diff --git a/src/utils/react/RenderCache.tsx b/src/utils/react/RenderCache.tsx
new file mode 100644
index 000000000..8562ae846
--- /dev/null
+++ b/src/utils/react/RenderCache.tsx
@@ -0,0 +1,46 @@
+import { ReactElement, useRef } from 'react';
+
+export interface RenderCacheProps {
+ items: T[];
+ renderKeys: (string | number)[];
+ getKey: (item: T) => string | number;
+ children: (item: T) => ReactElement;
+}
+
+/**
+ * RenderCache optimizes rendering of item lists by reusing
+ * previously rendered elements for unchanged items.
+ */
+export function RenderCache({
+ items,
+ renderKeys,
+ getKey,
+ children,
+}: RenderCacheProps): ReactElement {
+ // Store previous renders
+ const cacheRef = useRef