diff --git a/.changeset/great-papayas-obey.md b/.changeset/great-papayas-obey.md
new file mode 100644
index 0000000000..6f4b2c8f49
--- /dev/null
+++ b/.changeset/great-papayas-obey.md
@@ -0,0 +1,5 @@
+---
+"@hyperdx/app": patch
+---
+
+fix: persist column widths in search results table
diff --git a/packages/app/src/DBSearchPage.tsx b/packages/app/src/DBSearchPage.tsx
index ad6de5f7f2..7ed4f2d3a7 100644
--- a/packages/app/src/DBSearchPage.tsx
+++ b/packages/app/src/DBSearchPage.tsx
@@ -1314,6 +1314,14 @@ export function DBSearchPage() {
};
}, [chartConfig, searchedTimeRange]);
+ // Stable key for persisting column widths in localStorage. Scoped per saved
+ // search when one is loaded, else per source for ad-hoc searches.
+ const columnSizeTableId = savedSearchId
+ ? `db-search-saved-${savedSearchId}`
+ : searchedConfig.source
+ ? `db-search-source-${searchedConfig.source}`
+ : undefined;
+
const displayedColumns = useMemo(() => {
// `select` is typed as `string | DerivedColumn[]` upstream, but in the
// search page we always supply a string. Guard for type safety.
@@ -2284,6 +2292,7 @@ export function DBSearchPage() {
context={rowTableContext}
config={dbSqlRowTableConfig}
sourceId={searchedConfig.source}
+ tableId={columnSizeTableId}
onSidebarOpen={onSidebarOpen}
onExpandedRowsChange={onExpandedRowsChange}
enabled={isReady}
diff --git a/packages/app/src/components/DBRowTable.tsx b/packages/app/src/components/DBRowTable.tsx
index 5b4b297cbe..9d2c8c31e6 100644
--- a/packages/app/src/components/DBRowTable.tsx
+++ b/packages/app/src/components/DBRowTable.tsx
@@ -1530,6 +1530,7 @@ function DBSqlRowTableComponent({
initialSortBy,
variant = 'default',
enableSmallFirstWindow,
+ tableId,
}: {
config: BuilderChartConfigWithDateRange;
sourceId?: string;
@@ -1554,6 +1555,7 @@ function DBSqlRowTableComponent({
onSortingChange?: (v: SortingState | null) => void;
variant?: DBRowTableVariant;
enableSmallFirstWindow?: boolean;
+ tableId?: string;
}) {
const { data: me } = api.useMe();
const { toggleColumn, displayedColumns: contextDisplayedColumns } =
@@ -1863,6 +1865,7 @@ function DBSqlRowTableComponent({
getRowWhere={getRowWhere}
variant={variant}
onRemoveColumn={toggleColumn ? onRemoveColumnFromTable : undefined}
+ tableId={tableId}
/>
>
);
diff --git a/packages/app/src/components/DBSqlRowTableWithSidebar.tsx b/packages/app/src/components/DBSqlRowTableWithSidebar.tsx
index 718a43cbd5..083205df77 100644
--- a/packages/app/src/components/DBSqlRowTableWithSidebar.tsx
+++ b/packages/app/src/components/DBSqlRowTableWithSidebar.tsx
@@ -43,6 +43,7 @@ interface Props {
initialSortBy?: SortingState;
variant?: DBRowTableVariant;
enableSmallFirstWindow?: boolean;
+ tableId?: string;
}
export default function DBSqlRowTableWithSideBar({
@@ -63,6 +64,7 @@ export default function DBSqlRowTableWithSideBar({
initialSortBy,
variant,
enableSmallFirstWindow,
+ tableId,
}: Props) {
const { data: sourceData } = useSource({ id: sourceId });
const [rowId, setRowId] = useQueryState('rowWhere', parseAsStringEncoded);
@@ -142,6 +144,7 @@ export default function DBSqlRowTableWithSideBar({
collapseAllRows={collapseAllRows}
variant={variant}
enableSmallFirstWindow={enableSmallFirstWindow}
+ tableId={tableId}
/>
);
diff --git a/packages/app/src/components/__tests__/DBRowTable.test.tsx b/packages/app/src/components/__tests__/DBRowTable.test.tsx
index 406148bf99..ffb459b2f5 100644
--- a/packages/app/src/components/__tests__/DBRowTable.test.tsx
+++ b/packages/app/src/components/__tests__/DBRowTable.test.tsx
@@ -143,6 +143,60 @@ describe('RawLogTable', () => {
]);
});
});
+
+ describe('Column width persistence', () => {
+ const baseProps = {
+ displayedColumns: ['col1', 'col2'],
+ rows: [{ col1: 'value1', col2: 'value2' }],
+ isLoading: false,
+ dedupRows: false,
+ hasNextPage: false,
+ onRowDetailsClick: () => {},
+ generateRowId: () => mockRowWhereResult,
+ columnTypeMap: new Map(),
+ showExpandButton: false,
+ };
+
+ beforeEach(() => {
+ window.localStorage.clear();
+ });
+
+ it('applies stored column width when tableId is provided', () => {
+ // useLocalStorage stringifies under the same key the component reads.
+ window.localStorage.setItem(
+ 't1-column-sizes',
+ JSON.stringify({ col1: 250 }),
+ );
+
+ const { container } = renderWithMantine(
+