Skip to content

Svelte 5 insert row gives very poor performance #5758

@prince-jagani

Description

@prince-jagani

TanStack Table version

v9.0.0-alpha.10

Framework/Library version

Svelte 5.0.0-next.244

Describe the bug and the steps to reproduce it

Hello, I've been using Tanstack Table v9 with svelte 5 + sveltekit and getting weird performance issue.

This is the function to insert a new row at starting of the table. I'm updating table data and inserting a new row with websocket message that is coming every one second from server. Basically I'm inserting a new row every 1 second and it gives weird console warnings and after 50-60 seconds application starts lagging and eventually crashes. I've attached console log warnings below.

I've noticed some other important points related to this:

  1. If I insert a new row every 5 second instead of every 1 second, issue still persist
  2. If I have applied filters on the data such that no data will be in list then it do not lag.(e.g. if age is ranged 0 to 30 and filter it to min 31)
  3. As many rows I insert lag and pause time increases. Almost same time as the console warning
  4. Debouncing the rowModel = table.getRowModel(); isn't working at all
  • Tanstack Table Version: ^9.0.0-alpha.10
  • Svelte: ^5.0.0-next.244
  • Sveltekit: ^2.0.0
  • @sveltejs/vite-plugin-svelte: ^4.0.0-next.6

I assume that this is rendering related issue.. I've also attached snippet for how i'm rendering the table.

export const insertNewRow = (rowData) => {

		if (isAnimatingRow) return;
		animateInsertRow = true;
		isAnimatingRow = true;
		document.getElementById('main-layout').scrollTo({ top: 0, behavior: 'smooth' });

		data = [rowData, ...data.slice(0, 49)];

		setTimeout(() => {
			animateInsertRow = false;
			isAnimatingRow = false;
		}, 500);
	};

      //other code

$effect(() => {
		if (sorting || filters || data) {
			rowModel = table.getRowModel();
		}
	});
<div class="table select-none flex text-xs md:text-sm w-full">
	<div class="table-root sticky top-0 flex flex-col">
		<div
			class={`relative table-header grid border-b-2 border-border sticky top-[52px] z-10 lg:rounded-t-md`}
			style:grid-template-columns={`${tableGridLayout}`}
			bind:this={gridHeaderLayout}
		>
			{#each table.getHeaderGroups() as headerGroup, ind (ind)}
				{#each headerGroup.headers as header, index (index)}
					<div
						class="table-header-cell border-b border-borderLight bg-card px-4 h-10 flex items-center grow-1"
						class:sticky={header.column.columnDef.pinned}
						class:left-0={header.column.columnDef.pinned}
						class:z-10={header.column.columnDef.pinned}
						style:top={`${headerTopOffset}px`}
						style:left={`${header.column.columnDef.leftOffset || 0}px`}
						class:justify-end={index !== 0}
						class:border-r={index !== headerGroup.headers.length - 1}
					>
						{#if !header.isPlaceholder}
							{#if header.column.getCanSort()}
								<button
									class="flex gap-2"
									onclick={() => {
										console.log(sorting[0]);
										if (sorting[0] && sorting[0].id === header.column.columnDef.accessorKey) {
											updateExternalSort(
												header.column.columnDef.accessorKey,
												sorting[0] ? (sorting[0].desc ? 'asc' : 'none') : 'desc'
											);
										} else {
											updateExternalSort(header.column.columnDef.accessorKey, 'desc');
										}
									}}
								>
									{#if typeof header.column.columnDef.header === 'function'}
										{header.column.columnDef.header()}
									{:else}
										{header.column.columnDef.header}
									{/if}

									{#if header.column.getIsSorted().toString() === 'asc'}
										<PrimeSortUpFill />
									{:else if header.column.getIsSorted().toString() === 'desc'}
										<PrimeSortDownFill />
									{/if}
								</button>
							{:else if typeof header.column.columnDef.header === 'function'}
								{header.column.columnDef.header()}
							{:else}
								{header.column.columnDef.header}
							{/if}
						{/if}
					</div>
				{/each}
			{/each}
		</div>
		<div
			class={`relative flex-grow table-body ${rowModel.rows.length === 0 && 'h-[calc(100vh-359px)] sm:h-[calc(100vh-301px)] md:h-[calc(100vh-319px)]'}`}
			bind:this={tableBody}
		>
			{#if isLoading}
				<TableLoader gridLayout={tableGridLayout} columns={columnDef.length} />
			{:else if rowModel.rows.length === 0}
				<div class={`size-full min-h-full max-w-[100vw] lg:max-w-[calc(100vw-256px)] bg-card p-4`}>
					<div
						class="text-center p-4 border border-dashed min-h-full rounded-button text-xl flex items-center justify-center"
					>
						<span class="text-highlight">{notFound}</span>
					</div>
				</div>
			{:else}
				{#each rowModel.rows as row, rowIndex (row.original.id)}
					<div
						class={`table-row grid group`}
						style:grid-template-columns={tableGridLayout}
						animate:flip={{ duration: 300 }}
						in:fly={getTransition(rowIndex)}
						out:slide={{ duration: 300 }}
					>
						<!-- Maybe only this:
					in:fly="{{ y: rowIndex === 0 && animateInsertRow ? 50 : 0, duration: 300 }}" -->
						{#each row.getVisibleCells() as cell, cellIndex (cellIndex)}
							<div
								class="table-cell p-3 flex items-center border-b border-borderLight grow-1 group-hover:bg-modal"
								class:pinned={cell.column.columnDef.pinned}
								class:bg-background={cell.column.columnDef.pinned}
								class:border-b-0={rowIndex === rowModel.rows.length - 1}
								class:lg:rounded-bl-md={rowIndex === rowModel.rows.length - 1 && cellIndex === 0}
								class:lg:rounded-br-md={rowIndex === rowModel.rows.length - 1 &&
									cellIndex === row.getVisibleCells().length - 1}
								class:border-r={cellIndex !== row.getVisibleCells().length - 1}
								style:left={`${cell.column.columnDef.leftOffset || 0}px`}
								class:justify-end={cellIndex !== 0}
							>
								{#if cell.column.columnDef.component}
									{@const { component: CustomComponent, props } = cell.column.columnDef.cell(
										cell.getContext()
									)}
									<CustomComponent {...props} />
								{:else}
									{cell.getValue()}
								{/if}
							</div>
						{/each}
					</div>
				{/each}
			{/if}
		</div>
	</div>
</div>

Image

Your Minimal, Reproducible Example - (Sandbox Highly Recommended)

'

Screenshots or Videos (Optional)

No response

Do you intend to try to help solve this bug with your own PR?

None

Terms & Code of Conduct

  • I agree to follow this project's Code of Conduct
  • I understand that if my bug cannot be reliable reproduced in a debuggable environment, it will probably not be fixed and this issue may even be closed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions