Vue Embedded AnalyticsUpgrading to AG Studio 3

Version 3.0.0

New calculation, undo and redo, agent framework, server-side and design system features.

What's New Copy Link

AG Studio 3 introduces:

These features introduce certain breaking changes, as listed below.

Breaking Changes Copy Link

The full list of breaking changes in AG Studio 3. These entries are written to be read by an AI agent as well as by you, so each one is deliberately verbose: it spells out what changed, what an application relying on the old behaviour will see, and how to mitigate it.

To automate the upgrade, use the ag-update skill, which reads this migration guide, checks it against your repository, and produces a plan of the changes needed.

AI Agent Framework Copy Link

The ai property on AgStudioProperties has been rebuilt around the Studio Agent Framework, and a version 2 configuration does not carry over. This is a re-architecture rather than a rename: expect to reimplement what you passed to ai, rather than to adjust it.

In version 2 the property was an AgAiAssistant: one object that was both the connection to your LLM, through executeTurn, and the agent roster, through agents and primaryAgent. Those are now separate concerns. The property takes a harness - the thread and agent layer the chat panel renders from - and the LLM connection is an adapter the harness's agents run against.

The property is now an AgAiHarnessSetup: an AgAiHarness, or a function building one from the Studio API. createAiHarness builds Studio's own, and pairs each built-in agent with your adapter:

// Version 2: one object, holding both the provider connection and the roster.
const studioProperties = {
    ai: {
        executeTurn: (request) => myAssistant.executeTurn(request),
        agents: [...agStudioDefaultAgents, myAgent],
        primaryAgent: 'lead',
    },
};

// Version 3: a harness, given an adapter to run the agents against.
const studioProperties = {
    ai: ({ api }) => createAiHarness(api, { adapter }),
};

Four things changed shape underneath that, and each is documented in full in the AI section:

  • The provider connection is an adapter. AgAiAssistant has become AgLlmAdapter, with AgAiRequest, AgAiResponse and AgAiResponseHandler becoming AgLlmRequest, AgLlmResponse and AgLlmResponseHandler. executeTurn still runs one turn against your provider, but its stream now yields the AG-UI events of AgAiEvent in place of the status, item, part and delta events of AgAiStreamEvent, so a version 2 adapter's decoding half needs rewriting.
  • An agent is a definition plus a runner. AgAiAgent, with its type and its config: (params) => AgAiAgentConfig, has become a flat AgAiAgentDefinition paired with a runner that answers for it: directLlmRunner when Studio should run the loop against your adapter, clientToolRunner when you answer each turn, or your own run when the loop is yours.
  • Instructions and tools are resolved per run. They were read once from config(params); they are now callbacks re-read on every run, which is what lets a tool's schema carry live field and widget ids. A version 2 agent that built its instructions eagerly must move that work inside the callback.
  • Tools and delegates are objects, not names. tools took AgToolRef names and delegateAgents took agent ids. Both are now built: Studio's tools come from api.getAiTools() or the config builder's tools.studio, and delegation from tools.delegateTo([ids]). agStudioDefaultAgents has been replaced by the config builder's builtIn, which hands you the five Built-In Agents as definitions to pair with a runner.

Conversations are no longer part of report state: see AgReportState.ai under Removed APIs. Module registration is unchanged - AgStudioAiModule is still what opts the feature in.

Cross Filtering Copy Link

A cross filter selection now belongs to a group, and group is a required property. A group is a zero-based number that identifies which of a widget's selections is being set. A widget can hold one selection per group, and selections in different groups are independent of each other, so a widget that selects on several fields at once gives each field its own group.

group has been added as a required property to:

  • AgCrossFilterSelectionValues and AgCrossFilterSelectionRange, returned by AgWidgetApi.getCrossFilterSelections().
  • AgCrossFilterSelectionValuesState and AgCrossFilterSelectionRangeState, used in the crossFilter page state.
  • AgWidgetCrossFilterValueParams and AgWidgetCrossFilterRangeParams, passed to AgWidgetApi.toggleCrossFilter().

A custom widget that holds a single selection should pass group: 0:

api.toggleCrossFilter({ type: 'value', field, value, group: 0 });

Selections are resolved against the group they are set on.

Saved state from a version prior to 3.0.0 is migrated automatically on load: each selection that has no group takes its position in the widget's selection array, so the first selection becomes group 0, the second group 1, and so on. Selections that already carry a group keep it. You only need to update code that reads or sets cross filter selections directly.

Removed APIs Copy Link

  • AgReportState.ai has been removed. AI assistant conversations are no longer part of report state: they are owned by the agent harness, and are persisted by passing a history store (AgAiHistoryStore) in the AgAiHarnessConfig given to createAiHarness() - see Harness Overview.
  • AgPageState.schema has been removed. Use the report-level AgReportState.schema instead. Saved state from a version prior to 3.0.0 is migrated automatically on load: each page's schema fields are hoisted into the report-level schema.
  • CYCLIC_FRAGMENT_IDS, isGeneratedCalendar and isAdoptedCalendar have been removed from the package entry point with no replacement. They were internal helpers that were exported by mistake.
  • frame has been removed from AgWidgetConfig and AgWidgetDefinition, along with the AgWidgetFrameStyle type. Widget framing is now part of the formatting config and can be customised per widget instance through the border properties under format.widget (borderEnabled, borderWidth, borderColor, borderRadius). Remove any frame property from your widget configurations and definitions.
  • nullsFirst has been removed from AgCubeAxisLevelSort. Use nullPlacement instead, which names all four placements rather than the two a boolean can express. Replace nullsFirst: true with nullPlacement: 'nullsFirst' and nullsFirst: false with nullPlacement: 'nullsLast'. An axis level that set neither now takes the engine-wide AgNullHandlingOptions.nullPlacement, which defaults to 'nullsMin': a null sorts as the smallest value, so it comes first ascending and last descending. The previous default was not one rule but two, and which one you were getting depended on what the level sorted by: an axis level sorted by a dimension value behaved as 'nullsMax', and one sorted by a measure behaved as 'nullsLast'. To keep the previous ordering, set whichever of the two matches the level.
  • AgStudioModule now contains only moduleName and version, and moduleName is typed by AgStudioModuleName, which has been narrowed to the module names you can register. The other properties, and the types that described them, were internal implementation details: SingletonBean, AgLicense, ExternalModuleName and InternalModuleName have all been removed from the package entry point with no replacement. Modules are still registered in the same way.
  • A calculated column or measure no longer carries expression, columns and fieldsetDependencies directly. Its compiled form has moved to a calculation property (AgFieldCalculation), holding the compiled expression and the columns and fieldsets it depends on, and expression now holds either the authored expression string or its parsed form. Read the compiled shape through calculation, and treat expression as the authored definition.
  • getName() has been removed from field objects. Use AgWidgetApi.getFieldName(field) instead, which resolves the display name for any field, including a user rename and an implicit measure's aggregation label.
  • FieldSample, SampleValue and SampleOptions have been removed from the package entry point with no replacement. Nothing else in the public API referenced them.
  • Spreadable has been removed from the package entry point, and the type it fed has been rewritten. See AgWidgetFormFormatID under Updated APIs below.
  • createTitleGroup, createSubtitleGroup and createCaptionGroup have been removed from AgWidgetFormParams. Pass your own groups and controls to createTitleSection() instead, through its items builder, which appends them after the built-in title, subtitle and caption groups.
createTitleSection(() => [myGroup]);

Renamed APIs Copy Link

The chart series configuration interfaces have been renamed from …SeriesConfig to …SeriesTheme, for consistency with the other chart theme types. Only the names have changed; the shape of each type is the same. Update any imports that reference the old names:

  • AgAreaChartSeriesConfigAgAreaChartSeriesTheme
  • AgBubbleChartSeriesConfigAgBubbleChartSeriesTheme
  • AgLineChartSeriesConfigAgLineChartSeriesTheme
  • AgNightingaleChartSeriesConfigAgNightingaleChartSeriesTheme
  • AgRadarAreaChartSeriesConfigAgRadarAreaChartSeriesTheme
  • AgRadarLineChartSeriesConfigAgRadarLineChartSeriesTheme
  • AgRadialBarChartSeriesConfigAgRadialBarChartSeriesTheme
  • AgRadialColumnChartSeriesConfigAgRadialColumnChartSeriesTheme
  • AgScatterChartSeriesConfigAgScatterChartSeriesTheme

The AI schema context types have gained an AgAi prefix, again for consistency. Only the names have changed:

  • SchemaMetaAgAiSchemaMeta
  • TableMetaAgAiTableMeta
  • ColumnMetaAgAiColumnMeta
  • MeasureMetaAgAiMeasureMeta
  • CalculatedMetaAgAiCalculatedMeta
  • FieldMetaAgAiFieldMeta
  • RelationshipMetaAgAiRelationshipMeta
  • VocabularyMetaAgAiVocabularyMeta
  • FragmentsMetaAgAiFragmentsMeta
  • WidgetEntryAgAiWidgetEntry
  • WidgetSizingAgAiWidgetSizing

Updated APIs Copy Link

  • AgWidgetToolbarButton.icon is now typed AgStudioIcon. If changing the icon for a widget toolbar item, use the raw icon value instead. See Icons for the list of icon values.
  • AgExpressionFieldDefinition.expression now accepts a string as well as a parsed expression, so an expression can be authored as text. Code that writes one is unaffected; code that reads one must handle both forms.
  • AgEditableFieldKeys has gained a required expression: boolean key. A hand-written AgEditableFieldKeys literal needs that key added.
  • AgWidgetFormFormatID now reads `format.${'style' | 'title' | 'subtitle' | 'caption' | 'crossFilter'}.${string}`. It has gained format.caption.*.
  • AgBaseField, AgColumn, AgCalculatedColumn, AgMeasure, AgImplicitMeasure, AgField, AgWidgetField and AgBaseFieldDefinition have gained a third generic parameter for a field's context. It is defaulted, so ordinary use is unaffected; a mapped or conditional type written over the exact parameter list may need updating.
  • AgPivotCellKeySpec.fn now receives a fifth argument, isOthers, appended after the existing context argument so a four-argument function keeps reading context from the same position. A bucketed dimension substitutes null into the same tuple position for both the aggregated Other bucket and a member whose value is null, so a key function that ignores the new argument gives the two the same key.

Custom Data Engines Copy Link

These changes keep their existing types and change what a custom AgDataEngine or data source must do. The fan-out and anchor checks below are the exception: they apply whatever engine executes the query, so a dashboard on Studio's own engine sees those too.

  • Each request in a call must now resolve independently. Studio collects every widget's request for a render cycle into a single execute() or executeCube() call, so a call carries more requests than it did before. The requests come from unrelated callers, so one request's failure or cancellation must be reported as that request's own result and must not reject the promise returned for the whole call. An engine that rejects the call will hand empty results to every unrelated widget batched into it rather than raising an error.
  • AgRequestOptions.signal is now honoured. Studio aborts it when a request is superseded or times out, and discards any result that arrives after that. An engine can now be stopped mid-request where it never was before, and should propagate the signal into its own backend call.
  • Grid widget rows must carry a unique, stable identifier. A source that already carries its own identifier should name it as rowIdField in the result metadata, so Studio uses it directly rather than computing one.
  • Fan-out detection and the many-to-many anchor refusal now run for every query shape and every engine, including chart and native widget queries and any query resolved through resolveApiQuery. A query whose joins inflate its aggregates now reports a finding instead of silently inflated numbers. The anchor refusal can now reject a query it never reached before. A query can declare intensional duplication, through acceptFanout on the join clause.
  • Drill-across is also no longer dispatched to a caller-supplied engine which cannot carry it out. Such a query now reports its fan-out.
  • data.options.fanout.execute defaults to 'allow', so the query still runs its flat join and can still return inflated aggregates alongside the warning. Set execute: 'prevent' to reject such a query instead: it fails with a validation error and returns no data.
  • The base source of a query is now explicit. AgJoinClause carries leftSourceId, naming the source a join attaches to, and leftSourceAlias to disambiguate a self-join. A query's base source is joins[0].leftSourceId, falling back to joins[0].leftField.sourceId for a hand-authored clause that omits it. An engine that derived the base source for itself should read it from the clause.
  • A bucketed dimension's Other bucket now has an explicit marker. AgPivotColumnAxisEntry and AgPivotColumnNode carry isOthers, matching AgResultTuple. The bucket and a member whose value is null both present the same null, so a consumer telling the two apart must check this flag rather than testing the tuple's value against null.
  • A source that declares capabilities is called differently. getData receives a third options argument carrying one or more of paging, filter and sort. The engine trusts the response for a capability. Other capabilities are then provided by the built-in engine.

Theming Copy Link

Removed Theme Parameters Copy Link

The following parameters have been removed from AgStudioThemeParams. Studio never applied them, so setting them never changed anything on screen. Delete them from your theme; there is no replacement, because there was nothing to replace.

  • cardShadow
  • chartBorderRadius
  • chartButtonFontWeight
  • chartChromeBackgroundColor
  • chartChromeFontFamily
  • chartChromeFontSize
  • chartChromeFontWeight
  • chartChromeSubtleTextColor
  • chartChromeTextColor
  • chartForegroundColor
  • chartGroupedCategoryLineColor
  • chartInputBackgroundColor
  • chartInputBorder
  • chartInputBorderRadius
  • chartInputTextColor
  • chartPaletteDownFillColor
  • chartPaletteDownStrokeColor
  • chartPaletteNeutralFillColor
  • chartPaletteNeutralStrokeColor
  • chartPaletteUpFillColor
  • chartPaletteUpStrokeColor
  • chartTooltipSubtleTextColor
  • chromeBackgroundColor
  • dragAndDropImageBackgroundColor
  • dragAndDropImageBorder
  • dragAndDropImageNotAllowedBorder
  • focusErrorShadow
  • gridAutoHeightMinBodyHeight
  • gridBackgroundColor
  • gridBorderColor
  • gridBorderRadius
  • gridBorderWidth
  • gridCardShadow
  • gridCellEditingBorder
  • gridCellEditingShadow
  • gridCellHorizontalPaddingScale
  • gridCellWidgetSpacing
  • gridChromeBackgroundColor
  • gridColumnDragIndicatorColor
  • gridColumnDragIndicatorWidth
  • gridColumnDropCellBackgroundColor
  • gridColumnDropCellBorder
  • gridColumnDropCellDragHandleColor
  • gridColumnDropCellTextColor
  • gridColumnHoverColor
  • gridColumnSelectIndentSize
  • gridDialogBorder
  • gridDialogShadow
  • gridDragAndDropImageBackgroundColor
  • gridDragAndDropImageBorder
  • gridDragAndDropImageNotAllowedBorder
  • gridDragAndDropImageShadow
  • gridDragHandleColor
  • gridDropdownShadow
  • gridFindActiveMatchBackgroundColor
  • gridFindActiveMatchColor
  • gridFindMatchBackgroundColor
  • gridFindMatchColor
  • gridFocusErrorShadow
  • gridFocusShadow
  • gridFooterRowBorder
  • gridForegroundColor
  • gridFullRowEditInvalidBackgroundColor
  • gridHeaderCellBackgroundTransitionDuration
  • gridHeaderCellHoverBackgroundColor
  • gridHeaderCellMovingBackgroundColor
  • gridHeaderColumnBorder
  • gridHeaderColumnResizeHandleColor
  • gridHeaderColumnResizeHandleHeight
  • gridHeaderColumnResizeHandleWidth
  • gridHeaderVerticalPaddingScale
  • gridIconButtonActiveBackgroundColor
  • gridIconButtonActiveColor
  • gridIconButtonActiveIndicatorColor
  • gridIconButtonBackgroundColor
  • gridIconButtonBackgroundSpread
  • gridIconButtonBorderRadius
  • gridIconButtonColor
  • gridIconButtonHoverBackgroundColor
  • gridIconButtonHoverColor
  • gridInvalidColor
  • gridListItemHeight
  • gridMenuBackgroundColor
  • gridMenuBorder
  • gridMenuSeparatorColor
  • gridMenuShadow
  • gridMenuTextColor
  • gridModalOverlayBackgroundColor
  • gridPaginationPanelHeight
  • gridPanelBackgroundColor
  • gridPickerFieldHeight
  • gridPinnedColumnBorder
  • gridPinnedRowBackgroundColor
  • gridPinnedRowFontWeight
  • gridPinnedRowTextColor
  • gridPinnedSourceRowBackgroundColor
  • gridPinnedSourceRowFontWeight
  • gridPinnedSourceRowTextColor
  • gridPopupShadow
  • gridRangeHeaderHighlightColor
  • gridRangeSelectionBackgroundColor
  • gridRangeSelectionBorderColor
  • gridRangeSelectionBorderStyle
  • gridRangeSelectionChartBackgroundColor
  • gridRangeSelectionChartCategoryBackgroundColor
  • gridRangeSelectionHighlightColor
  • gridRowDragIndicatorColor
  • gridRowDragIndicatorWidth
  • gridRowGroupIndentSize
  • gridRowLoadingSkeletonEffectColor
  • gridRowNumbersSelectedColor
  • gridRowVerticalPaddingScale
  • gridSelectCellBackgroundColor
  • gridSelectCellBorder
  • gridSelectedRowBackgroundColor
  • gridSubtleTextColor
  • gridTextColor
  • gridToggleButtonHeight
  • gridToggleButtonOffBackgroundColor
  • gridToggleButtonOnBackgroundColor
  • gridToggleButtonSwitchBackgroundColor
  • gridToggleButtonSwitchInset
  • gridToggleButtonWidth
  • gridTooltipBackgroundColor
  • gridTooltipBorder
  • gridTooltipErrorBackgroundColor
  • gridTooltipErrorBorder
  • gridTooltipErrorTextColor
  • gridTooltipTextColor
  • gridValueChangeDeltaDownColor
  • gridValueChangeDeltaUpColor
  • gridValueChangeValueHighlightBackgroundColor
  • gridWidgetContainerHorizontalPadding
  • gridWidgetContainerVerticalPadding
  • gridWidgetHorizontalSpacing
  • gridWidgetVerticalSpacing
  • gridWrapperBackgroundColor
  • headerBackgroundColor
  • headerHeight
  • headerVerticalPaddingScale
  • studioAiPanelFieldBadgeBackgroundColor
  • studioAiPanelFieldBadgeColor
  • studioAiPanelWidgetBadgeBackgroundColor
  • studioAiPanelWidgetBadgeColor
  • studioPanelBackgroundColor
  • tabSelectedUnderlineColor
  • tabSelectedUnderlineTransitionDuration
  • tabSelectedUnderlineWidth
  • tooltipErrorBackgroundColor
  • tooltipErrorBorder
  • tooltipErrorTextColor

The four AI panel badge parameters are a slightly different case from the rest of this list: they did once apply. Inline field and widget references in the AI panel are now distinguished by icon rather than by colour, and take their colours from the surrounding theme, so there is no badge left for these to colour.

Renamed Theme Parameters Copy Link

These theme params have been renamed so that each name describes what it actually affects. The corresponding CSS custom property changes with it, so application stylesheets that read the generated --ag- variables need updating too:

Old ParamNew Param
headerFontFamily
(--ag-header-font-family)
studioPanelHeaderFontFamily
(--ag-studio-panel-header-font-family)
headerFontSize
(--ag-header-font-size)
studioPanelHeaderFontSize
(--ag-studio-panel-header-font-size)
headerFontWeight
(--ag-header-font-weight)
studioPanelHeaderFontWeight
(--ag-studio-panel-header-font-weight)
headerLineHeight
(--ag-header-line-height)
studioPanelHeaderLineHeight
(--ag-studio-panel-header-line-height)
headerTextColor
(--ag-header-text-color)
studioWidgetTitleTextColor
(--ag-studio-widget-title-text-color)
studioChartLabelColor
(--ag-studio-chart-label-color)
studioLinearGaugeLabelColor
(--ag-studio-linear-gauge-label-color)
studioFiltersPanelCardSubtleHoverColor
(--ag-studio-filters-panel-card-subtle-hover-color)
studioFiltersPanelCardIconSubtleHoverColor
(--ag-studio-filters-panel-card-icon-subtle-hover-color)
studioPanelGroupTitleBarBackgroundColor
(--ag-studio-panel-group-title-bar-background-color)
studioPanelGroupTitleBarTextColor
(--ag-studio-panel-group-title-bar-text-color)
studioPanelTabHeaderBackgroundColor
(--ag-studio-panel-tab-header-background-color)
tabBarBackgroundColor
(--ag-tab-bar-background-color)
studioPanelTabHeaderSelectedBackgroundColor
(--ag-studio-panel-tab-header-selected-background-color)
tabSelectedBackgroundColor
(--ag-tab-selected-background-color)
studioPanelTabHeaderSelectedShadow
(--ag-studio-panel-tab-header-selected-shadow)
tabSelectedShadow
(--ag-tab-selected-shadow)
studioWidgetSelectIconBackgroundColor
(--ag-studio-widget-select-icon-background-color)
studioWidgetSelectionItemBackgroundColor
(--ag-studio-widget-selection-item-background-color)
studioWidgetSelectIconBorder
(--ag-studio-widget-select-icon-border)
studioWidgetSelectionItemBorder
(--ag-studio-widget-selection-item-border)
studioWidgetTileHeight
(--ag-studio-widget-tile-height)
studioWidgetSelectionItemHeight
(--ag-studio-widget-selection-item-height)
studioWidgetTileWidth
(--ag-studio-widget-tile-width)
studioWidgetSelectionItemWidth
(--ag-studio-widget-selection-item-width)
widgetContainerHorizontalPadding
(--ag-widget-container-horizontal-padding)
studioFiltersPanelContainerHorizontalPadding
(--ag-studio-filters-panel-container-horizontal-padding)
widgetContainerVerticalPadding
(--ag-widget-container-vertical-padding)
studioFiltersPanelContainerVerticalPadding
(--ag-studio-filters-panel-container-vertical-padding)
widgetHorizontalSpacing
(--ag-widget-horizontal-spacing)
studioFiltersPanelItemHorizontalSpacing
(--ag-studio-filters-panel-item-horizontal-spacing)
widgetVerticalSpacing
(--ag-widget-vertical-spacing)
studioFiltersPanelItemVerticalSpacing
(--ag-studio-filters-panel-item-vertical-spacing)

Only the names have changed. Each param keeps its previous value and effect, so the rendered UI is unchanged once the new names are in place.

Theme Type Changes Copy Link

  • AgStudioThemeParams now directly includes the properties in OptionalThemeParams. OptionalThemeParams has been removed as no longer needed.
  • AgStudioDefaultThemeParams has been removed. Use AgStudioThemeParams instead.

Grid Widget Dependency Copy Link

ag-studio-vue3 now depends on ag-grid-vue3, which is installed automatically alongside it.

Behaviour Changes Copy Link

The full list of behaviour changes in AG Studio 3.

Dashboards and Widgets Copy Link

  • Switching between Edit and View Mode no longer saves, discards or reapplies state. Changes made in View Mode now persist when returning to Edit Mode. Applications that relied on the reset can reproduce it by calling getState() before switching to View Mode and setState() when switching back - see Modes and State.
  • Text and image widgets now inherit the page level widget appearance settings like every other widget type. They previously never drew a border or corner radius, whatever the page was set to. To keep the previous look on an individual widget, turn its border off in the Widget Appearance section of the widget's format settings, or set borderEnabled: false and borderRadius: 0 under format.widget in saved state. Saved state is not migrated for this change, so an existing dashboard that enables page level widget borders will start showing borders on its text and image widgets; dashboards that leave page level widget borders off, which is the default, look identical.

Cross Filter Interaction Copy Link

  • Cross filtering from a chart widget with a legend field now filters on the category and the legend value together. Clicking a point or segment previously applied a condition on the category field alone; it now applies a single combined condition matching the clicked category and its legend series. A combined condition is shown as a single read-only card in the Cross Filters section of the Filters Panel, rather than one card per field. Charts with no legend field are unaffected.
  • The Cross Highlight cross filtering mode has been removed for 100% Stacked Bar and 100% Stacked Column widgets, because a proportional highlight cannot be drawn on a normalised stack. Those two widgets now offer Cross Filter and None, and a new one defaults to Cross Filter. A report saved before 3.0.0 is migrated when it loads, so a 100% stacked widget set to Cross Highlight becomes Cross Filter and redraws under the condition instead of highlighting within it. State you author yourself is not migrated, so set format.crossFilter to 'filter' there. Every other bar, column, pie and doughnut widget keeps the option and still defaults to it.

Charts Copy Link

  • A chart that splits its series by a legend field now limits how many series it draws. Legend members are ranked by the first plotted measure and the rest are dropped, where a chart whose legend produced more series than the chart's own limit previously failed to render at all. The budget counts series rather than members, so two plotted measures halve how many members fit. Set format.seriesLimit.max to choose your own budget, or format.seriesLimit.enabled: false to plot every member.

Grid Widget Copy Link

  • A grid widget whose row request fails now reports the failure through Studio's error handling and shows its no-data state. It previously logged to the console and left the widget under its loading overlay, so a failed request looked like one still in flight.
  • The server-side grid widget now bounds how many row blocks it keeps and how many row requests it runs at once. Sustained scrolling refetches evicted blocks where it previously served them from a cache that grew without limit. Set maxBlocksInCache in AgGridWidgetOptions to choose your own bound.