A portal behind a login has a measurement problem that has little to do with its technology: the public field dataset that fuels almost every discussion about load time does not know these pages. It only admits what a search engine could reach as well. Everything that appears after signing in falls out structurally. At the same time no full-page cache applies there, because every response is bound to a session. Anyone who wants to steer speed in a SaaS product or a customer portal therefore has to produce the numbers themselves. This article shows which metrics carry in the signed-in area, how to set up your own collection, and why long tables and hours-long sessions need different limits than a home page.
Key takeaways
- The public field dataset only admits a page when it is publicly discoverable and has enough visitors; for an origin the documentation names two requirements (Chrome UX Report). Behind a login, typically neither is met.
- The thresholds themselves stay valid: 2.5 seconds for the largest contentful paint, 200 milliseconds for interaction response, 0.1 for layout shift, each at the 75th percentile (web.dev). Only the data source is missing.
- The replacement is your own collection inside the product: record metrics per view type rather than per address, because portal addresses carry tenant, case and record numbers.
- Long tables hit the node count: up to Lighthouse 12 the DOM size audit warned above roughly 800 nodes in the body and reported an error above roughly 1,400 nodes (Lighthouse). Since Lighthouse 13 of October 2025 a DOM size insight without published node counts has taken its place; as an order of magnitude both figures remain usable.
- Inner pages carry more JavaScript than home pages - a mobile median of 660 KB against 632 KB (Web Almanac 2025). In the signed-in area the lever therefore sits in runtime, not in images.
- A budget per view type makes the measurement fit for acceptance: list view, detail view, form and report each get their own limits instead of one value for the entire portal.
Why no field data exists behind a login
The field dataset that reports and tools rely on collects measurements from real browser sessions. An origin is only included if it meets two requirements (Chrome UX Report): it must be publicly discoverable and have enough visitors. Discoverability is judged by the same criteria as a search engine applies. A view that only comes into existence after signing in does not meet that - regardless of how many people use it every day. In a portal, the difference between field data and your own measurement therefore does not decide accuracy, it decides whether any numbers exist at all.
The documentation names three states in which a page counts as not publicly discoverable: a response with a status other than 200 after all redirects, a robots header carrying noindex, or a robots meta tag with the same value in the document. Those three states are the norm in a portal: a request without a session ends on the login screen, and the signed-in area carries noindex for good reason. For SaaS applications this means the public part - home page, pricing, documentation - shows up in the dataset, while the product itself stays empty.
What this means for prioritisation
Which thresholds still apply
The missing data source changes nothing about the target values. For a good experience the largest contentful paint should happen within 2.5 seconds of the page starting to load, interaction response should be 200 milliseconds or less, and layout shift should stay at 0.1 or below (web.dev). These are thresholds for the assessment, not measurements - they apply regardless of who does the measuring.
More important than the threshold is the rule behind it: the recommendation is to look at the 75th percentile of page loads, segmented across mobile and desktop devices (web.dev). An average hides exactly the sessions that matter - the account with 4,000 rows in the list, the tablet in the warehouse, the connection out in the field. Anyone who carries the Core Web Vitals over into a portal therefore carries over not only the numbers but the way they are evaluated.
The difference between a public page and a signed-in area affects the reference frame more than the metric itself. The comparison below shows what shifts.
| Metric or dimension | Public page | Signed-in area |
|---|---|---|
| Data source | aggregated browser field dataset | your own collection inside the product |
| Reference frame | origin and individual address | view type, role and size class |
| Page load | first delivery, often from cache | response per session, no full-page cache |
| Interaction response | few interactions per load | hundreds of interactions per session |
| Layout stability | images, banners, consent layer | rows and filters loaded after the fact |
| Time frame | the provider's rolling window | your own window, aligned to releases |
There is one more peculiarity: many portals switch views without a full page load. The documentation of the dataset spells this out - such route transitions look like new page views to the user, while for the browser and the underlying platform APIs the entire experience belongs to the first load (Chrome UX Report). Knowing how to measure soft navigations is therefore not a detail in a portal but the precondition for the second and third view to produce values at all.
Your own collection in the signed-in area
The collection starts with a decision that is hard to correct later: what do you group by? Portal addresses carry tenant, case and record numbers; grouping by address gives you thousands of groups with one measurement each. What works is grouping by view type - list view, detail view, form, report - plus the role and, where relevant, the size class of the tenant. That is also the data-minimising route: what measurement without consent allows depends largely on how little a collection says about the individual person.
// Record metrics per view type, not per address
const view = () => ({
type: document.body.dataset.viewType, // list | detail | form | report
role: document.body.dataset.role,
rows: Number(document.body.dataset.rows || 0)
});
const buffer = [];
const record = (name, value) =>
buffer.push({ name, value: Math.round(value), ...view() });
function evaluate(type, entry) {
if (type === 'largest-contentful-paint') record('lcp', entry.startTime);
if (type === 'event' && entry.interactionId) record('inp', entry.duration);
if (type === 'long-animation-frame') record('laf', entry.duration);
}
const observe = (type, extra = {}) => {
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) evaluate(type, entry);
}).observe({ type, buffered: true, ...extra });
};
observe('largest-contentful-paint');
observe('event', { durationThreshold: 40 });
observe('long-animation-frame');
// Send in one batch when the view is left
addEventListener('visibilitychange', () => {
if (document.visibilityState !== 'hidden' || buffer.length === 0) return;
navigator.sendBeacon('/api/rum', JSON.stringify(buffer.splice(0)));
});Sending belongs at the end of a view, not in the middle of it. sendBeacon delivers on leaving without holding up the transition; for regular interim batches there is now a way to send measurement data with a delay that needs no timer of your own. In both cases the same principle applies: the collection must not degrade the very metric it collects.
View type instead of address
List view, detail view, form and report behave differently in technical terms. Only this grouping turns thousands of individual values into a distribution you can read.
Data volume per load
Row count, column count and filter state belong with every measurement. Without them you cannot tell later whether a view is slow or a tenant is simply large.
Age of the session
How long the tab has been open explains part of the response times. The value costs nothing and separates a fresh load from a view that has lived in the same tab for hours.
Response time per action
Sorting, filtering, expanding: a portal produces hundreds of interactions per session. Only their distribution shows which action is expensive and how often it occurs.
The server's share
A timestamp from the application in the response header separates backend waiting from work in the browser. Without that split, experience shows that optimisation lands in the wrong place.
Nodes and memory
Node count in the body and allocated memory belong in the record. Both grow in production without a single line of code changing.
The order that has proven itself
Long tables and the node count
Portals show lists, and lists grow with the account. Up to Lighthouse 12 the DOM size audit warned when the body element contained more than roughly 800 nodes and reported an error above roughly 1,400 nodes (Lighthouse); since Lighthouse 13 of October 2025 it has been replaced by a DOM size insight for which no node counts are published. As an order of magnitude the figures remain usable: a table with 200 rows and twelve columns amounts to 2,400 cells before header row, controls and borders are added. That order of magnitude is therefore typically exceeded on the first page of results.
For comparison: across pages measured on mobile, the median number of elements per page fell from 653 in 2022 to 594 in 2024, and the 90th percentile from 1,832 to 1,716 (Web Almanac 2024). A portal view with 2,400 cells therefore sits well above what is measured on public pages at the 90th percentile. Knowing how to reduce DOM size is no side topic in a portal - and because many rows only come into being through connected third-party systems, the node count is often decided in the backend.
- Put only the visible window into the tree and swap it while scrolling; fix the row height beforehand so the scrollbar stays calm.
- Render cells without their own behaviour as text instead of nested elements with icon, tooltip and menu.
- Handle per-row actions through one shared listener on the table instead of one listener per row.
- Fill expandable sections only when they open; a collapsed section with 40 fields otherwise counts in full.
- For very long lists, check
content-visibilityso the browser may skip areas outside the viewport.
The number missing from the test
Long-running sessions need different limits
A public page is opened, read and left. A portal stays open - through the morning, often through the whole working day. That shifts the weight: the first page load happens once, interaction response happens hundreds of times. Memory that goes unnoticed on a single load adds up over hours; how frontend memory leaks come about is therefore an operational question in the signed-in area, not a footnote.
The composition of the transferred data shifts as well. In a mobile median, inner pages accounted for 660 KB of JavaScript against 632 KB on home pages, while home pages on average request around 45.8 percent more bytes than inner pages (Web Almanac 2025). For a portal that means: total weight goes down, script volume does not - the lever sits in the runtime of the code. And because every response is bound to a session, no full-page cache applies; which layers are left is shown by a look at how cache layers stack in an editorial system.
{
"budget_per_view_type": [
{ "view": "list", "rows_up_to": 200, "ttfb_ms": 400, "inp_ms": 200, "max_nodes": 1400 },
{ "view": "list", "rows_up_to": 2000, "ttfb_ms": 600, "inp_ms": 200, "max_nodes": 1400 },
{ "view": "detail", "rows_up_to": 0, "ttfb_ms": 400, "inp_ms": 200, "max_nodes": 800 },
{ "view": "form", "rows_up_to": 0, "ttfb_ms": 400, "inp_ms": 150, "max_nodes": 800 },
{ "view": "report", "rows_up_to": 0, "ttfb_ms": 900, "inp_ms": 200, "max_nodes": 800 }
],
"percentile": 75,
"window_days": 30,
"split_by": ["device_class", "role", "size_class"],
"session": { "max_memory_mb": 250, "runtime_hours": 8 }
}From measurement to acceptance
A measurement only becomes a tool once someone can read something from it that carries a decision. For B2B portals that usually means: a budget per view type, a percentile instead of an average, and a size class the value applies to. If the weight sits in the backend, the route leads through server optimisation; if it sits in the browser, through the runtime of the code. Which case applies is decided by the split between waiting and work - and that split is in the data as soon as the server's share is recorded.
- Name the view types and mark them in the markup so every measurement has a group.
- Roll out the collection and let it run for two weeks before anything is changed - without a baseline there is no proof later.
- Build the 75th percentile per view type, segmented across mobile and desktop devices.
- Set budgets that fit the size class of the account and write them into the acceptance criteria rather than into a report.
- Check against the budget on every release and record the comparison with the previous run.
The most common finding in such collections is not a single slow page but an action that is used often and sits just above the threshold. Knowing how to improve interaction response is therefore usually the point with the highest return per effort in a portal - well ahead of the first page load, which a user experiences once a day.
Behind a login there is no external number to compare yourself against. There is only your own measurement - and it is worth exactly as much as the grouping it was collected with.
The effort for the setup is manageable: mark the view types, register three observers, provide an endpoint of your own. The real work is the evaluation - and it pays off wherever hundreds of people work with the same list every day. If you would rather shorten the start: in a performance analysis we record the views one by one and set the budgets together with your team.
noindex, does not meet that - regardless of how often it is used.Related Articles
Templates, Not the Home Page: Where Load Time Is Made
Teams optimize the home page while visitors land on category, product and article pages. How to measure by template and prove the order of work.
Performance Measurement Without Consent: What Is Allowed
Section 25 TDDDG comes before the GDPR: when a Vitals measurement is device access, how narrow the exemption is and what stays measurable without consent.
Frontend Memory Leaks: When Long Sessions Slow Down
The interface starts fast and turns sluggish after twenty minutes: how front-end memory leaks arise, how to measure them over time and how to get rid of them.