ag-grid-community
ag-grid-community is the MIT-licensed core of AG Grid, a browser data grid for rendering and interacting with large tabular datasets. It provides virtualized rows and columns, sorting, column filters, editing, pagination, selection, CSV export, custom renderers and editors, theming, accessibility support, and client-side or infinite row models. The core package works with plain JavaScript and TypeScript; React, Angular, and Vue projects install matching wrapper packages. Features are delivered as modules, so an app can register the full Community bundle or only the pieces it uses.
Install it when the interface truly needs a data grid and Community's feature boundary covers the product roadmap. For ordinary tables or teams unwilling to absorb frequent API migrations and a possible commercial upgrade, a headless or smaller grid is the safer fit.
Use it if
- You need a full application data grid with virtualization, sorting, filtering, editing, selection, pagination, and CSV export in one maintained package
- You expect thousands of rows or expensive cells and need a grid that recycles DOM rows instead of rendering a normal HTML table in full
- You need one grid model across plain JavaScript, React, Angular, and Vue, with TypeScript definitions and framework-specific wrappers
- You want to start under MIT and have a supported commercial upgrade path if pivoting, grouping, Excel export, or the server-side row model becomes necessary
- You only need a responsive table with sorting over a few dozen rows: AG Grid's module registration, API object, state model, and styling system are disproportionate to that job
- You need row grouping, aggregation, pivoting, master/detail, tree data, Excel export, clipboard operations, range selection, formulas, or integrated charts but cannot buy an Enterprise license; the repository feature table puts those outside Community
- You want headless markup and complete control over every DOM element: AG Grid owns the grid structure and behavior, while TanStack Table provides state and leaves rendering to you
- You cannot budget for recurring migration work: modules became the standard setup in version 33, React gained AgGridProvider in version 35.1, and the project ships frequent majors with dedicated migration guides
- You expect Community's infinite row model to perform Enterprise-style grouping and aggregation on the server; infinite scrolling can fetch blocks, but the advanced server-side row model is commercial
Setup reality
For plain JavaScript or TypeScript, install ag-grid-community, register modules before creating a grid, and give the container an explicit height because the grid fills its parent. AllCommunityModule is the easiest start but includes every Community feature; production bundles should register only the modules actually used. Version 36 development builds can call enableDevValidations before creating any grid to get full configuration messages, but the docs recommend leaving that out of production. React, Angular, and Vue use separate ag-grid-react, ag-grid-angular, or ag-grid-vue3 packages and the versions should stay aligned with ag-grid-community. Since React 35.1, the documented setup passes modules through AgGridProvider; in server-rendered React apps, registration belongs on the client because the grid requires the DOM. The current Theming API accepts objects such as themeQuartz.withParams and does not require legacy theme CSS. If you retain pre-v33 CSS themes, follow the legacy path consistently instead of combining both approaches. Choose the row model before building data access: client-side loads rows into the browser, infinite calls a datasource in blocks, and the advanced server-side row model is Enterprise. Infinite caches need maxBlocksInCache or a long session can retain many blocks. Finally, audit exports: Community provides CSV, not Excel, and spreadsheet programs can interpret formula-like CSV cell values unless processCellCallback neutralizes untrusted leading characters.
Patterns
Create a typed grid with the Community bundlecreate-basic-grid
import type { GridOptions } from 'ag-grid-community';
import { AllCommunityModule, ModuleRegistry, createGrid } from 'ag-grid-community';
ModuleRegistry.registerModules([AllCommunityModule]);
const options: GridOptions<{ id: number; name: string }> = {
rowData: [{ id: 1, name: 'Ada' }],
columnDefs: [{ field: 'id' }, { field: 'name' }],
};
const api = createGrid(document.querySelector('#grid')!, options);The #grid element needs an explicit height. Register modules before createGrid; AllCommunityModule favors convenience over the smallest bundle.
Register only the features in useregister-selected-modules
import {
ClientSideRowModelModule,
CsvExportModule,
ModuleRegistry,
PaginationModule,
RowSelectionModule,
TextFilterModule,
} from 'ag-grid-community';
ModuleRegistry.registerModules([
ClientSideRowModelModule,
TextFilterModule,
PaginationModule,
RowSelectionModule,
CsvExportModule,
]);Missing modules produce coded errors in production. Call enableDevValidations only in development for full messages and an on-grid diagnostic overlay.
Apply sortable and filterable column defaultsconfigure-columns
const gridOptions = {
columnDefs: [
{ field: 'name', pinned: 'left' },
{ field: 'country', filter: 'agTextColumnFilter' },
{ field: 'revenue', type: 'numericColumn' },
],
defaultColDef: {
sortable: true,
filter: true,
resizable: true,
flex: 1,
minWidth: 120,
},
};flex sizing and explicit width compete; once a user manually resizes a flex column, its flex behavior is disabled.
Display currency while keeping numeric dataformat-and-parse-values
const currency = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
});
const priceColumn = {
field: 'price',
editable: true,
valueFormatter: ({ value }) => currency.format(value ?? 0),
valueParser: ({ newValue }) => Number(newValue),
};A formatter changes display and exported text, not the underlying value. Validate Number(newValue) before accepting user input to avoid storing NaN.
Validate and persist a cell edithandle-cell-edits
const gridOptions = {
columnDefs: [{ field: 'quantity', editable: true }],
onCellValueChanged: async ({ data, oldValue, newValue, node, api }) => {
try {
await saveQuantity(data.id, newValue);
} catch {
node.setDataValue('quantity', oldValue);
api.flashCells({ rowNodes: [node], columns: ['quantity'] });
}
},
};AG Grid updates client data before this event runs. A failed server write needs an explicit rollback and a user-visible error path.
Enable multi-row selection and read it backselect-rows
const gridOptions = {
rowSelection: {
mode: 'multiRow',
checkboxes: true,
headerCheckbox: true,
},
getRowId: ({ data }) => String(data.id),
};
const selectedRecords = api.getSelectedRows();Stable getRowId values preserve identity across sorting and data refreshes. Without them, selection can attach to the wrong row after updates.
Apply incremental row changesupdate-row-data
api.applyTransaction({
add: [{ id: 3, name: 'Grace' }],
update: [{ id: 1, name: 'Ada Lovelace' }],
remove: [{ id: 2, name: 'Old name' }],
});Define getRowId for reliable update and remove matching. Replacing rowData is simpler for small datasets but discards more grid work.
Connect a search box to the quick filterquick-filter
const input = document.querySelector('#search');
input.addEventListener('input', (event) => {
api.setGridOption('quickFilterText', event.target.value);
});Quick filter searches displayed text across columns on the client. Debounce very large client-side datasets and do not confuse it with server search.
Add client-side paginationpaginate-rows
const gridOptions = {
pagination: true,
paginationPageSize: 25,
paginationPageSizeSelector: [25, 50, 100],
rowData,
columnDefs,
};Client-side pagination still loads the whole rowData array into the browser. Use an infinite datasource when the dataset should be fetched in blocks.
Fetch rows in blocks with the Community infinite modelload-infinite-rows
const gridOptions = {
rowModelType: 'infinite',
cacheBlockSize: 100,
maxBlocksInCache: 5,
datasource: {
async getRows(params) {
try {
const page = await fetchRows(params.startRow, params.endRow);
params.successCallback(page.rows, page.totalRows);
} catch {
params.failCallback();
}
},
},
};Register InfiniteRowModelModule when cherry-picking. Community infinite scrolling does not include Enterprise server-side grouping or aggregation.
Persist and restore grid statesave-grid-state
localStorage.setItem('orders-grid-state', JSON.stringify(api.getState()));
const saved = localStorage.getItem('orders-grid-state');
const gridOptions = {
initialState: saved ? JSON.parse(saved) : undefined,
rowData,
columnDefs,
};initialState is read when the grid is created. Stored state can outlive renamed columns, so version the storage key and handle invalid JSON.
Export CSV while neutralizing spreadsheet formulasexport-safe-csv
api.exportDataAsCsv({
fileName: 'orders.csv',
processCellCallback: ({ value }) => {
const text = String(value ?? '');
return /^[=+\-@]/.test(text) ? `'${text}` : text;
},
});Community exports CSV, not native Excel files. Prefix untrusted formula-like values because spreadsheet applications may execute them when the file opens.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| @tanstack/table-core | npm | Choose it when you want headless table state and will build the markup, virtualization, and visual design yourself |
| tabulator-tables | npm | Choose it for a batteries-included open-source grid without AG Grid's Community versus Enterprise feature split |
| gridjs | npm | Choose it for a smaller searchable and sortable table with a simpler API and lighter feature expectations |
| handsontable | npm | Choose it for spreadsheet-style editing and formulas after reviewing its non-MIT licensing terms for your use |