Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCountry SelectCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputEmoji PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone SelectTree Select
Forms
Address InputBooking WidgetCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage GalleryImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadChat WidgetConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeTerminalText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowOrder History PagePost Listing WizardPricing PageProfile PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAgency PageAI Product PageAppointment Booking PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageHome Services PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePortfolio PagePress PageProduct Tour PagePromo PageReal Estate PageReferral PageResearch Report PageRestaurant PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageStorefront PageSwitch PageTemplate Gallery PageTravel PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

Tables & Lists

Editable Grid

Tables & Lists

Editable Grid React component for Tailwind CSS v4

Deal PipelineKanban
Live props
Appearance
density
rowHeader
tone
color
radius
showToolbar
showErrors
Layout
height480px
frozenColumns1
resizableColumns
Behavior
allowAddRows
allowDeleteRows
allowPasteNewRows
validateOn
responsive
locale
virtualThreshold100
State
readOnly
Code
import { EditableGrid } from "@/components/ui/editable-grid"

<EditableGrid />
  1. Home
  2. Components
  3. Tables & Lists
  4. Editable Grid
Pro

Editable Grid React component for Tailwind CSS v4

v1.0.1·Updated in 1.7.0

A React editable grid that works like a spreadsheet: typed cells edited in place, range selection, Excel copy and paste, undo, validation.

Click a cell to select it and the outline glides there; Shift+click, Shift+arrows or a mouse drag select a range, a row number selects the row, a column header the column, and Ctrl/⌘+A everything. Type to replace a cell, or press Enter, F2 or double-click to edit it: Enter commits and goes down (Shift+Enter up), Tab commits and goes right, wrapping to the next row (Shift+Tab left), Escape cancels, clicking elsewhere commits. The grid is one Tab stop: outside an edit, Tab moves on to the next control. Selects and dates open their picker; Space toggles checkboxes. Arrows move, Ctrl/⌘+arrows jump to the edges, Home / End go to the row ends, Ctrl/⌘+Home / End to the grid corners, PageUp / PageDown by a page. Ctrl/⌘+C copies the range as tab separated values Excel and Sheets understand, Ctrl/⌘+X cuts, Ctrl/⌘+V pastes a block from the active cell (adding rows when needed), Delete or Backspace clears, Ctrl/⌘+Z undoes and Shift+Ctrl/⌘+Z or Ctrl+Y redoes. Bad values stay in the cell with a red corner and the message under the active cell; the toolbar's error count jumps to the next one. Drag a header edge to resize a column, double-click it to fit. Hover a row number (or right-click, Shift+F10) for insert, duplicate and delete. On a phone the grid scrolls both ways with the first column frozen: tap a cell to select it, tap it again (or the row number) to edit the whole row in a bottom sheet, long press a row number for its actions.

Editable Grid is a Pro React component in the React table and list components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add editable-grid, together with Button, Checkbox, Currency Input, Date Picker, Drawer and 16 more and the npm packages @tanstack/react-virtual, motion and lucide-react.

Add to a project

Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup

npx wingo-ui@latest add editable-grid

Editable Grid is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans

Usage

Import EditableGrid from @/components/ui/editable-grid.

import { useState } from "react"
import { EditableGrid, type GridColumn, type GridRow } from "@/components/ui/editable-grid"

type Product = GridRow & { name: string; category: string; price: number | null; stock: number | null; active: boolean }

const columns: GridColumn<Product>[] = [
  { id: "name", header: "Product", required: true, width: 240 },
  {
    id: "category",
    header: "Category",
    type: "select",
    options: [
      { value: "licenses", label: "Licenses" },
      { value: "services", label: "Services" },
    ],
  },
  { id: "price", header: "Price before tax", type: "currency", currency: "USD", min: 0 },
  { id: "stock", header: "Stock", type: "number", step: 1, min: 0 },
  { id: "active", header: "Active", type: "checkbox" },
]

export function PriceList() {
  const [rows, setRows] = useState<Product[]>([
    { id: "p-1", name: "Acme CRM Business license (month)", category: "licenses", price: 49, stock: 120, active: true },
    { id: "p-2", name: "Setup and training (hour)", category: "services", price: 65, stock: null, active: true },
  ])
  return (
    <EditableGrid
      title="Price list 2026"
      columns={columns}
      value={rows}
      onValueChange={setRows}
      locale="en-US"
      createRow={() => ({ id: crypto.randomUUID(), name: "", category: "licenses", price: null, stock: null, active: true })}
      labels={{ addRow: "New product" }}
    />
  )
}

Props

  • columns*
    GridColumn<R>[]

    The columns: { id (the row key), header, type (text, number, currency, select, date, checkbox), width, minWidth, options, currency, decimals, min, max, step, required, validate, readOnly (or a function of the row), align, format, placeholder, frozen }. Columns are not virtualized; a grid here has fewer than 30.

  • value
    R[]

    The rows, controlled. Every row needs a unique string id. Pair with onValueChange.

  • defaultValuedefault "[]"
    R[]

    The starting rows while uncontrolled.

  • onValueChange
    (rows: R[]) => void

    Every change to the rows: edits, pastes, cleared ranges, added, deleted and restored rows.

  • onCellChange
    (change: GridCellChange) => (void | false | Promise<unknown>)

    One committed cell ({ rowId, columnId, value, previous }). Return false or a rejected promise to put the old value back (save to a server and roll back on failure).

  • onCellsChange
    (changes: GridCellChange[]) => (void | false | Promise<unknown>)

    Batches: a paste, a cleared range, a row saved from the sheet, undo and redo. Same revert contract as onCellChange.

  • createRowdefault "() => ({ id: crypto.randomUUID() })"
    (index: number) => R

    Defaults for new rows (the add row button, inserts, rows a paste appends). An id is generated when it returns none.

  • title
    ReactNode

    The toolbar's title, e.g. the sheet name.

  • toolbarActions
    ReactNode

    Your own buttons at the end of the toolbar (export, import, filters).

  • emptyStatedefault "an EmptyState with Add the first row"
    ReactNode

    Shown under the headers when there are no rows.

  • labelsdefault "DEFAULT_EDITABLE_GRID_LABELS"
    Partial<EditableGridLabels>

    Every string in English: addRow, deleteRows(count, n), insertAbove, insertBelow, duplicate, edit, delete, undo, redo, errors(count, n), required, invalidNumber, invalidDate, notAnOption, tooLow, tooHigh, invalidStep, editRow(index), save, cancel, close, pasted(rows, cols), copied, cleared, rowsDeleted and the rest. Pass only the keys you translate.

  • densitydefault "default"
    "compact" | "default"

    Row height 32 / 40px (40 / 44px on touch screens) and cell text 13 / 14px.

  • rowHeaderdefault "number"
    EditableGridRowHeader

    number, checkbox or none. The leading column: row numbers (click selects the row, hover shows the row menu) or checkboxes for bulk actions (the toolbar offers Delete N rows).

  • tonedefault "neutral"
    "neutral" | "brand" | "success" | "warning" | "danger" | "info"

    The active cell outline, the range and the checkboxes.

  • colordefault ""
    string

    Any CSS color for the outline, the range and the checkboxes. Replaces the tone.

  • radiusdefault "md"
    "none" | "sm" | "md" | "lg"

    Corner radius of the panel: 0 / 12 / 16 / 24px.

  • showToolbardefault true
    boolean

    The toolbar row: title, error count, bulk delete (rows picked by their header or checkbox), undo / redo, and Add row once the rows scroll. A read-only grid with nothing to show there hides it.

  • showErrorsdefault "both"
    "cell" | "summary" | "both"

    cell: invalid cells get a tint, a red corner with the message and the message under the active cell. summary: an error count in the toolbar that jumps to the next one. both: the two.

  • heightdefault 480
    number

    Max height of the scroller in px; the header sticks inside it. 0 grows with the rows and never virtualizes.

  • frozenColumnsdefault 1
    number

    Columns that stay at the start edge while you scroll sideways (next to the row header), with a shadow once scrolled.

  • resizableColumnsdefault true
    boolean

    Drag a header's end edge to resize a column; double-click it to fit the content.

  • allowAddRowsdefault true
    boolean

    The Add row button and row, inserts from the row menu, and Enter on the last cell adding a row.

  • allowDeleteRowsdefault true
    boolean

    Delete in the row menu and the row sheet, and bulk delete in the toolbar.

  • allowPasteNewRowsdefault true
    boolean

    A paste taller than the rows left appends rows; off, the extra lines are dropped.

  • validateOndefault "change"
    "change" | "blur"

    change: the cell editor flags a bad value while you type. blur: only once it is committed. Committed cells are always checked (required, type, min / max / step, options, validate) and bad values are kept so they can be fixed.

  • readOnlydefault false
    boolean

    Selection and copy only: no editing, paste, add or delete. On phones the row sheet shows the row read-only with a Close button.

  • responsivedefault true
    boolean

    Below 768px a second tap edits the whole row in a bottom sheet and the row menu is an action sheet.

  • localedefault ""
    string

    Numbers, money, dates and paste parsing ("1,234.50" is 1234.5 in en-US, "1.234,50" in ro-RO). Empty uses the UIProvider or LocaleProvider locale, then the browser's. The paste parsers are exported too (see Parts).

  • virtualThresholddefault 100
    number

    Above this many rows only the visible ones render (fixed row height). Needs a height; 0 always virtualizes.

  • rowReadOnly
    (row: R) => boolean

    Rows that can be selected and copied but not edited (a locked invoice line, an approved timesheet).

  • defaultSelection
    GridSelection

    The selection on mount: { anchor: { rowId, columnId }, focus: { rowId, columnId } }.

  • onSelectionChange
    (selection: GridSelection | null) => void

    Fires when the active cell or the range changes.

  • setApi
    (api: EditableGridApi) => void

    The imperative API: focusCell(ref), startEditing(ref?), addRow(values?, index?) returning the id, deleteRows(ids), undo(), redo(), getSelection(), copySelection(), editRow(id) (opens the row form).

  • classNames
    Partial<Record<EditableGridSlot, string>>

    Extra classes for the parts: toolbar, title, scroller, table, header, corner, columnHeader, row, rowHeader, cell, editor, selection, active, add, empty.

  • className
    string

    Extra classes for the panel, merged last.

Parts

  • parseClipboardGrid

    function

    parseClipboardGrid(text) splits a copied block into rows of cells: tab separated from Excel and Google Sheets, or a ; / , CSV when every line has the same number of fields.

  • coerceCellValue

    function

    coerceCellValue(text, column, locale?) turns a pasted or typed string into the column's value ("1,234.50" to 1234.5 in en-US, a date string to a Date, an option label to its value). Text that does not parse stays text, so the cell shows it as invalid.

  • DEFAULT_EDITABLE_GRID_LABELS

    constant

    The English copy of the grid, menus and announcements. Spread it into labels when you translate only a few keys.

  • GridColumn

    type

    One column: id, header, type, width, minWidth, options, currency, decimals, min, max, step, required, validate, readOnly, align, format, placeholder and frozen.

  • EditableGridApi

    type

    The imperative handle setApi hands you, for toolbar buttons outside the grid: focusCell, startEditing, addRow, deleteRows, undo, redo, getSelection, copySelection and editRow.

Examples

Paste from Excel

A tab separated block lands from the active cell, adds rows and parses numbers and dates in the grid's locale.

2,000 rows

Only the visible rows render; scroll and watch the frame counter.

Validation

Required, min / max, options and a custom rule, with the error summary jumping to the next one.

Bulk delete with undo

rowHeader="checkbox": pick rows, delete them from the toolbar, undo from the toast.

Timesheet

Hour columns and a computed, read-only total through format.

Phone row sheet

On phones a second tap edits the whole row in a bottom sheet (a dialog on desktop).

Dependencies

@tanstack/react-virtualmotionlucide-reactbuttoncheckboxcurrency-inputdate-pickerdrawerdropdown-menuempty-stateinputnumber-inputselecttooltipuse-controllable-stateuse-localeuse-long-pressuse-media-querycommercedate-utilsmotionnumber-formattonesui-config

Files

  • components/ui/editable-grid.tsx

FAQ

Is the React Editable Grid component free?

The preview, props and docs are free; copying the source needs a Pro plan. See plans.

How do I install Editable Grid?

Run npx wingo-ui@latest add editable-grid. It also adds the Wingo UI items it builds on (Button, Checkbox, Currency Input, Date Picker, Drawer and 16 more) and the npm packages @tanstack/react-virtual, motion and lucide-react. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/editable-grid.

How does Editable Grid behave on phones?

Below 768px a second tap edits the whole row in a bottom sheet and the row menu is an action sheet. The responsive prop controls this and is on by default.

See also

Data TableInvoice line itemsCSV ImportSpreadsheetlibTable

More React table and list components

ListMembers tablePermissions MatrixTask ListVirtual ListKanban
All 11 React table and list components

Articles using this component

  • React Editable Table: Spreadsheet Grid or Inline Edit

    Component guides · Nov 5, 2026 · 9 min read

PreviousDeal PipelineNext Kanban
Price list 2026 · Acme Software
Acme CRM Business license (month)
Licenses
$49.00
8%
120
01/01/2026
Yes
Acme CRM Enterprise license (month)
Licenses
$89.00
8%
60
01/01/2026
Yes
Setup and training (hour)
Services
$65.00
8%
01/15/2026
Yes
E-invoicing integration
Services
$1,500.00
8%
03/01/2026
Yes
Data migration
Services
$2,800.00
8%
03/01/2026
Yes
Priority support (month)
Support
$120.00
8%
40
02/01/2026
Yes
Printed user guide
Hardware
$12.90
4%
350
09/01/2026
Yes
Advanced reports module (month)
Licenses
$19.00
8%
182
08/09/2026
Yes
Recurring billing module (month)
Licenses
$15.00
8%
61
08/06/2026
Yes
Inventory module (month)
Licenses
$24.00
8%
117
01/02/2026
Yes
Extra seat (month)
Licenses
$9.00
8%
156
02/04/2026
Yes
100 GB storage (month)
Licenses
$6.00
8%
163
07/25/2026
No
SMS pack 1,000
Licenses
$39.00
8%
82
09/08/2026
Yes
SMS pack 10,000
Licenses
$299.00
8%
377
04/21/2026
Yes
Field sales mobile app (month)
Licenses
$12.00
8%
166
08/08/2026
Yes
Sales process consulting (hour)
Services
$95.00
8%
11/09/2026
No
Online training (hour)
Services
$60.00
8%
04/02/2026
Yes
Workflow automation setup
Services
$950.00
8%
05/21/2026
Yes
Excel data import
Services
$650.00
8%
07/19/2026
Yes
Online store integration
Services
$2,200.00
8%
11/01/2026
Yes
Carrier integration (UPS)
Services
$1,200.00
8%
10/27/2026
Yes
Carrier integration (FedEx)
Services
$1,200.00
8%
02/14/2026
Yes
Database audit
Services
$1,800.00
8%
10/17/2026
Yes
Custom reports (hour)
Services
$75.00
8%
06/17/2026
Yes
Standard support (month)
Support
$49.00
8%
09/22/2026
Yes
Weekend support (month)
Support
$89.00
8%
11/19/2026
Yes
On-site visit
Support
$350.00
8%
02/01/2026
No
Annual maintenance
Support
$1,200.00
8%
11/26/2026
Yes
Dedicated daily backup (month)
Support
$29.00
8%
06/05/2026
Yes
Barcode scanner
Hardware
$89.00
4%
379
01/25/2026
Yes
Label printer
Hardware
$249.00
8%
354
07/07/2026
Yes
Label roll 2 x 1.2 in
Hardware
$5.50
4%
364
11/24/2026
Yes
Thermal paper 2.25 in (roll)
Hardware
$1.90
4%
381
06/26/2026
Yes
Sales rep tablet
Hardware
$399.00
4%
97
11/23/2026
Yes
Tablet case
Hardware
$19.00
4%
39
11/14/2026
Yes
Laminated quick guide
Hardware
$4.90
4%
129
10/20/2026
Yes
Printed admin manual
Hardware
$19.00
8%
375
07/02/2026
Yes
Portable POS terminal
Hardware
$449.00
8%
242
03/06/2026
Yes
Cash drawer
Hardware
$99.00
4%
360
01/10/2026
Yes
Digital scale 66 lb
Hardware
$189.00
8%
365
08/23/2026
Yes
Document scanner
Hardware
$279.00
8%
100
03/13/2026
No
Acme CRM Starter license (month)
Licenses
$25.00
8%
41
02/26/2026
Yes
Setup workshop (day)
Services
$1,200.00
8%
02/27/2026
Yes
Qualified digital certificate
Services
$69.00
8%
04/05/2026
Yes
E-signature (year)
Licenses
$49.00
8%
35
01/08/2026
Yes
ERP migration support
Services
$3,600.00
8%
11/16/2026
Yes
Shelf label kit (100 pcs)
Hardware
$12.00
8%
232
10/19/2026
Yes
Total stock value$566,522.00

last change: no changes · 0 errors · 47 rows · locale=(en-US from LocaleProvider) · virtualThreshold=100 · validateOn=change · allowPasteNewRows=true · resizableColumns=true · responsive=true