You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CodeSandbox可调整组件网格实现方案及React同类功能开发指引

Great question! Let's break this down into two clear parts: how CodeSandbox implements its resizable component grids and windows, and then the actionable technical directions to build a similar experience in React.

How CodeSandbox Implements Resizable Grids & Windows

CodeSandbox’s resizable layout isn’t magic—it’s a combination of custom layout logic, event handling, and state management tailored for their use case:

  • Core Layout Foundation: They use a mix of CSS Flexbox and custom grid calculations for the base structure. Each resizable panel (editor, preview, console) is a container with dynamic dimensions controlled by inline styles or CSS variables.
  • Drag Interaction Logic: The splitters (those thin bars between panels) listen for mousedown, mousemove, and mouseup events. When you click and drag a splitter, it tracks the mouse position in real time, recalculates the width/height of adjacent panels, and updates their styles instantly. They use requestAnimationFrame to smooth out the resizing motion and avoid jank.
  • State Sync: All panel sizes and positions are stored in a global state store (they use a custom internal state management system, similar to Redux or Zustand). This ensures that layout changes are consistent across the entire app, even when panels are nested or rearranged.
  • Boundary Constraints: To prevent panels from being dragged to zero width/height or becoming too large, they enforce minimum and maximum size limits for each panel. They also handle responsive breakpoints to adjust layouts automatically on smaller screens.
Technical Direction for Building a Resizable Grid in React

You’ve got two main paths here: using battle-tested libraries for speed, or building custom logic for full control. Let’s cover both:

1. Use Mature Libraries (Fastest Implementation)

If you want to skip reinventing the wheel, these libraries are perfect for VS Code/CodeSandbox-style layouts:

  • react-resizable-panels: A modern, accessible library designed specifically for nested resizable panels. It supports vertical/horizontal splits, remembers panel sizes, and integrates seamlessly with React’s state model. It’s the closest out-of-the-box solution to VS Code’s layout.
  • react-split-pane: A classic, lightweight library for basic split panels. It’s great for simpler layouts (like two or three panels) and lets you configure min/max sizes, splitter styles, and more with minimal code.
  • react-grid-layout: If you need panels that can both resize and drag-rearrange (like a dashboard), this library is ideal. It supports responsive breakpoints, drag-and-drop reordering, and custom grid configurations.

2. Build Custom Logic (Full Control)

If you need a fully tailored solution, here’s how to approach it:

  • Step 1: Lay the Foundation
    Start with a Flexbox or CSS Grid layout. Wrap each resizable panel in a container, and add a dedicated splitter element (a thin div) between panels to act as the drag handle.
  • Step 2: Implement Drag Handling
    Attach mousedown listeners to your splitters. When triggered, capture the initial mouse position and panel sizes, then add mousemove and mouseup listeners to the document to track the drag. Calculate the offset on each mousemove and update the panel sizes (use state hooks like useState or a global store for nested layouts).
    Example snippet for a basic horizontal split:
    import { useState, useCallback } from 'react';
    
    const ResizableSplit = () => {
      const [leftPanelWidth, setLeftPanelWidth] = useState(60); // Percent
    
      const handleDragStart = useCallback((e) => {
        e.preventDefault();
        const startX = e.clientX;
        const initialWidth = leftPanelWidth;
    
        const handleDrag = (moveEvent) => {
          const deltaX = moveEvent.clientX - startX;
          const newWidth = initialWidth + (deltaX / window.innerWidth) * 100;
          // Enforce min/max constraints
          if (newWidth >= 20 && newWidth <= 80) {
            setLeftPanelWidth(newWidth);
          }
        };
    
        const handleDragEnd = () => {
          document.removeEventListener('mousemove', handleDrag);
          document.removeEventListener('mouseup', handleDragEnd);
        };
    
        document.addEventListener('mousemove', handleDrag);
        document.addEventListener('mouseup', handleDragEnd);
      }, [leftPanelWidth]);
    
      return (
        <div style={{ display: 'flex', height: '100vh', width: '100%' }}>
          <div style={{ width: `${leftPanelWidth}%`, background: '#f5f5f5' }}>
            Editor Panel
          </div>
          <div
            style={{ width: '4px', background: '#ccc', cursor: 'col-resize' }}
            onMouseDown={handleDragStart}
          />
          <div style={{ flex: 1, background: '#ffffff' }}>
            Preview Panel
          </div>
        </div>
      );
    };
    
    export default ResizableSplit;
    
  • Step 3: Manage State for Complex Layouts
    For nested panels or multi-grid layouts, use a global state manager like Zustand or Redux to store all panel dimensions. This avoids prop drilling and keeps layout state consistent across components.
  • Step 4: Optimize Performance
    • Use useCallback to cache drag handlers and prevent unnecessary re-renders.
    • Wrap size updates in requestAnimationFrame to ensure smooth resizing.
    • Add will-change: width (or height) to panel styles to hint the browser about upcoming layout changes.
  • Step 5: Polish Accessibility & Edge Cases
    • Add keyboard support (e.g., arrow keys to adjust panel sizes) and ARIA attributes to splitters for screen readers.
    • Handle window resize events to recalculate panel sizes and maintain layout proportions.
    • Persist layout state to localStorage so users’ preferred panel sizes are saved between sessions.

内容的提问来源于stack exchange,提问作者Shivansh Tyagi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:28:19