Optimizing First Input Delay (FID) for 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1175
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Optimizing FID (First Input Delay) for 1C-Bitrix

FID (First Input Delay) — the time from the first user interaction with a page until the moment the browser begins processing that interaction. Since March 2024, Google has replaced FID with INP (Interaction to Next Paint), which measures all interactions, not just the first. In practice, optimization is the same for both metrics.

Threshold: FID < 100 ms, INP < 200 ms. On heavy Bitrix sites, INP can reach 500–1500 ms.

Why the browser doesn't respond to a click

The browser is single-threaded: while the main thread is busy executing JavaScript, it cannot process input events. The user clicks a button — the click is queued and waits for the JS to complete its current task.

Sources of long tasks (Long Tasks, > 50 ms) in Bitrix:

  • Loading and parsing large JS bundles: jQuery + plugins + components = 500 KB — 1 MB
  • Initializing sliders, mask fields, maps, widgets on DOMContentLoaded
  • Heavy event handlers: catalog filter, cart recalculation
  • Synchronous AJAX requests (block the thread)

Diagnosing Long Tasks

Chrome DevTools → Performance → record while loading the page. Red bars over Tasks — these are Long Tasks > 50 ms. Click on a task — DevTools will show the call stack: which script occupied the main thread.

For INP: DevTools → Performance → enable "Web Vitals" → interact with the page → find INP events.

Console monitoring:

const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
        if (entry.duration > 50) {
            console.warn('Long task:', entry.duration.toFixed(1) + 'ms', entry);
        }
    }
});
observer.observe({ type: 'longtask', buffered: true });

Bundle splitting and code splitting

Standard Bitrix loads all JS on every page: jQuery, catalog plugins, cart scripts, sliders, maps — everything at once. A page with 1 MB JS executes it entirely on load.

Transition to code splitting: each component loads only the necessary scripts:

// Load slider only if there's a slider on the page
if (document.querySelector('.main-slider')) {
    import('./swiper.min.js').then(({ default: Swiper }) => {
        new Swiper('.main-slider', { /* ... */ });
    });
}

// Map only on contacts page
if (document.querySelector('#map')) {
    import('./ymaps-init.js');
}

In the Bitrix context — via \Bitrix\Main\Page\Asset::addJs() in specific component templates, not in header.php.

Defer and async for scripts

<!-- Synchronous — blocks HTML parsing -->
<script src="/bitrix/js/plugin.js"></script>

<!-- defer — loads in parallel, executes after HTML parsing -->
<script src="/bitrix/js/plugin.js" defer></script>

<!-- async — loads and executes as early as possible -->
<script src="/bitrix/js/analytics.js" async></script>

defer — for scripts that need the DOM (component initialization). async — for independent scripts (analytics, ad tags).

In Bitrix, JS files added via \Bitrix\Main\Page\Asset::addJs() are output without defer. To add the attribute — custom implementation via OnEndBufferContent hook or template override.

Heavy event handlers

An event handler that performs a synchronous DOM recalculation on 200 elements blocks the thread for the duration of that recalculation. INP will equal the handler time.

Improvement patterns:

Debounce for frequent events (search input, filter change):

let debounceTimer;
searchInput.addEventListener('input', function() {
    clearTimeout(debounceTimer);
    debounceTimer = setTimeout(() => {
        doSearch(this.value);
    }, 300);
});

Breaking heavy operations with setTimeout / scheduler.postTask:

async function processLargeList(items) {
    for (let i = 0; i < items.length; i += 50) {
        const chunk = items.slice(i, i + 50);
        processChunk(chunk);
        // Yield control to browser between batches
        await new Promise(resolve => setTimeout(resolve, 0));
    }
}

Web Workers for computations: if the event handler needs heavy calculations (sorting, filtering a large data array) — move to Web Worker. Runs in a separate thread, doesn't block the UI.

Third-party scripts

JivoSite, MetrikaTag, Google Analytics, social media pixels — each adds JS executed in the main thread. With 5–10 third-party scripts, the cumulative startup load can be 200–500 ms of Long Tasks.

Strategy:

  1. Load third-party scripts via async or after load event
  2. Use requestIdleCallback for non-critical scripts
  3. Check if all connected widgets are needed — often there are unused ones
// Load analytics after idle
window.addEventListener('load', () => {
    requestIdleCallback(() => {
        const script = document.createElement('script');
        script.src = 'https://analytics-provider.com/tag.js';
        script.async = true;
        document.head.appendChild(script);
    });
});

Timeline for optimization

Task Timeline Effect
Audit Long Tasks via DevTools 0.5 day Problem understanding
Convert scripts to defer 1 day INP −50–200 ms
Defer third-party scripts 0.5 day INP −100–300 ms
Debounce search and filters 1 day Filter INP −200–500 ms
Code splitting for heavy components 3–5 days Catalog pages INP −200–500 ms
Refactor heavy event handlers 2–5 days INP < 200 ms

A good result for a Bitrix store is INP < 200 ms. This is achieved with a total JS bundle < 200 KB (after parse) and no Long Tasks > 100 ms on interaction.