Skip to content

Conversation

@ZeRego
Copy link
Collaborator

@ZeRego ZeRego commented Jan 27, 2026

Closes: PROD-2731

Description:

Implemented virtualized rendering for the explore sidebar to improve performance with large numbers of explores. This replaces the previous static rendering approach with a more efficient virtualized list that only renders items visible in the viewport.

Key improvements:

  • Added VirtualizedExploreList component using @tanstack/react-virtual for efficient DOM rendering
  • Optimized data processing with single-pass categorization and pre-sorting
  • Implemented debounced search for better performance during typing
  • Refactored ExploreGroup into a more focused GroupHeader component
  • Reduced unnecessary re-renders with proper memoization

This change significantly improves performance and responsiveness when navigating projects with many explores or explore groups.

Now:

CleanShot 2026-01-27 at 13.50.20.mp4 (uploaded via Graphite)

Before:

CleanShot 2026-01-27 at 13.51.47.mp4 (uploaded via Graphite)

How to test this locally with 2000+ explores:

import { type SummaryExplore } from '@lightdash/common';

// Mock data generator for 2000 explores to test search performance
export const generateMockExplores = (count: number = 2000): SummaryExplore[] => {
    const explores: SummaryExplore[] = [];
    
    const tableTypes = ['users', 'orders', 'products', 'transactions', 'events', 'sessions', 'analytics', 'marketing', 'sales', 'support'];
    const prefixes = ['customer', 'vendor', 'internal', 'external', 'staging', 'raw', 'processed', 'aggregated'];
    const suffixes = ['daily', 'weekly', 'monthly', 'historical', 'current', 'snapshot', 'summary', 'detail'];
    
    for (let i = 0; i < count; i++) {
        const tableType = tableTypes[i % tableTypes.length];
        const prefix = prefixes[Math.floor(i / tableTypes.length) % prefixes.length];
        const suffix = suffixes[Math.floor(i / (tableTypes.length * prefixes.length)) % suffixes.length];
        
        const name = `${prefix}_${tableType}_${suffix}_${String(i).padStart(4, '0')}`;
        const label = `${prefix.charAt(0).toUpperCase() + prefix.slice(1)} ${tableType.charAt(0).toUpperCase() + tableType.slice(1)} ${suffix.charAt(0).toUpperCase() + suffix.slice(1)} ${i}`;
        
        explores.push({
            name,
            label,
            tags: [`${prefix}`, `${tableType}`, `table_${i}`],
            // First 500 explores have no group, rest are grouped
            groupLabel: i < 500 ? undefined : `${prefix.charAt(0).toUpperCase() + prefix.slice(1)} Tables`,
            description: `Mock table for testing search performance - ${label}`,
            schemaName: `schema_${Math.floor(i / 100)}`,
            databaseName: `database_${Math.floor(i / 500)}`,
            type: undefined,
            warnings: Math.random() > 0.9 ? [{
                type: 'FIELD_ERROR' as const,
                message: 'Sample warning for testing'
            }] : undefined,
        });
    }
    
    return explores;
};

And modify useExplores hook

import { generateMockExplores } from './mockExploresData';

const getExplores = async (projectUuid: string, filtered?: boolean) => {
    // For testing search performance - use mock data with 2000 tables
    // Set this to true to simulate the performance issue
    const USE_MOCK_DATA = true;
    
    if (USE_MOCK_DATA) {
        // Simulate API delay
        await new Promise(resolve => setTimeout(resolve, 500));
        return generateMockExplores(2000);
    }
    
    return lightdashApi<ApiExploresResults>({
        url: `/projects/${projectUuid}/explores?filtered=${
            filtered ? 'true' : 'false'
        }`,
        method: 'GET',
        body: undefined,
    });
};

Copy link
Collaborator Author

ZeRego commented Jan 27, 2026

This stack of pull requests is managed by Graphite. Learn more about stacking.

@ZeRego ZeRego requested a review from rephus January 27, 2026 13:52
@ZeRego ZeRego changed the title perf: virtualize explore sidebar list fix(perf): virtualize explore sidebar list Jan 27, 2026
@linear
Copy link

linear bot commented Jan 27, 2026

@github-actions
Copy link

Your preview environment pr-19741 has been deployed.

Preview environment endpoints are available at:

@github-actions
Copy link

Preview Environment

🌐 URL: https://lightdash-preview-pr-19741.lightdash.okteto.dev

📋 Logs: View in GCP Console

🔧 SSH: ./scripts/okteto-ssh.sh 19741

Copy link
Collaborator

@rephus rephus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested locally, works great !

@rephus
Copy link
Collaborator

rephus commented Jan 27, 2026

Linked issue https://linear.app/lightdash/issue/CENG-2731 doesn't seem to be valid 🤔

@ZeRego ZeRego merged commit b9a221e into main Jan 27, 2026
21 checks passed
@ZeRego ZeRego deleted the perf_optimize_explore_sidebar_search_and_categorization branch January 27, 2026 14:38
lightdash-bot pushed a commit that referenced this pull request Jan 27, 2026
## [0.2380.1](0.2380.0...0.2380.1) (2026-01-27)

### Bug Fixes

* **perf:** virtualize explore sidebar list ([#19741](#19741)) ([b9a221e](b9a221e))
@lightdash-bot
Copy link
Collaborator

🎉 This PR is included in version 0.2380.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants