Skip to main content

Play+ Performance Helper: playperf

Introduction

In the Play+ ecosystem, performance is not a feature to be added later—it's a foundational commitment. We believe great design must feel fast. This helper is based on the concept of Performance by Default, where speed, responsiveness, and stability are architected and enforced from day one.

A performant application builds user trust and delight. This directly supports our core design pillars by creating an Engaging experience that feels fluid and responsive, and an Adaptive interface that performs well across all devices and network conditions. By ensuring a fast, stable experience, we also make our products more Intuitive and less frustrating to use.


Package Info

The Play+ performance helper and its associated CI configurations are included by default in the Golden Path starter kit.

Description

Package / PathGolden Path (Recommended)
Pre-installed/system/play.perf.ts
Uplift Pathnpm install @playplus/perf

Folder Reference

The performance helper and its configuration follow our standardized folder structure for core system logic.

File / DirectoryPurpose & Guidelines
system/play.perf.tsThe core performance helper. It provides utilities for deferring tasks and monitoring real-world performance.
config/play.performance.config.jsonUser-overridable configuration for performance budgets (Lighthouse) and monitoring settings.
reports/performance/The git-ignored directory where Lighthouse CI reports are saved for local inspection.

Helper - Pillars Alignment

The playperf helper is a direct implementation of our core design pillars, focused on the user's perception of speed.

PillarHow This Helper Aligns
EngagingPrimary Pillar: Ensures snappy interactions and fluid animations, which are key to a delightful and engaging user experience.
AdaptiveEnforces performance budgets that ensure the application is fast and responsive on a wide range of devices and networks.
IntuitiveA fast and stable application feels more predictable and intuitive, as users aren't left waiting or dealing with layout shifts.

Helper Overview

The playperf toolchain is a combination of automated guardrails and developer-facing utilities designed to abstract the plumbing of performance management. Instead of manually configuring performance budgets or writing complex code to defer non-critical tasks, developers can rely on the Play+ system to handle it.

It automates performance quality control in two key ways:

  • Preventative Guardrails: In the CI/CD pipeline, playperf automatically runs Lighthouse audits against every pull request. If key metrics (like LCP or CLS) exceed the configured budgets, the build fails, preventing performance regressions from ever reaching production.

  • Developer Empowerment Utilities: It provides a simple runtime helper with methods like defer() and monitor() that make it trivial to implement common performance patterns without writing boilerplate code.

The goal is to make high performance the default path, not an extra chore.


Config Options

Global performance budgets and settings are managed in config/play.performance.config.json. These values are consumed by the CI pipeline and runtime helpers.

Config Key Table

Config KeyDefault ValueDescriptionRecommended Value
lighthouse.lcp2500Lighthouse budget for Largest Contentful Paint (ms).2500
lighthouse.cls0.1Lighthouse budget for Cumulative Layout Shift.0.1
lighthouse.inp200Lighthouse budget for Interaction to Next Paint (ms).200
lighthouse.maxBundleSize250Lighthouse budget for initial JS bundle size (KB, gzipped).250
enforce.blockBuildOnFailtrueIf true, the CI build will fail if performance budgets are exceeded.true
enforce.enableBundleDifftrueIf true, PRs will be flagged with any unexpected changes to bundle size.true
monitor.webVitalstrueIf true, enables the real-user monitoring of Core Web Vitals via playperf.monitor.true
monitor.sentrytrueEnables integration with Sentry Performance for transaction tracking.true
assist.highlightHeavyComponentstrueIn dev mode, flags components that are computationally expensive or re-rendering unnecessarily.true

Helper Methods

Method: defer

Schedules a function to run during browser idle time, preventing it from blocking critical rendering.

defer(callback: () => void): void

Method: monitor

Initializes real-user monitoring of Core Web Vitals, sending data to a specified reporting function.

monitor(options: { onReport: (metric) => void }): void

Usage Examples

React: Deferring a Non-Critical Script

This example shows how to defer the initialization of an analytics script so it doesn't interfere with the initial page load.

import React, { useEffect } from 'react';
import { playperf } from '../../system/play.perf';
import { initializeAnalytics } from '../lib/analytics';

function App() {
useEffect(() => {
// Defer the analytics script to run after the main thread is free.
playperf.defer(() => {
initializeAnalytics({ apiKey: '...' });
});
}, []);

return (
// ... your application components
);
}

Angular: Monitoring Real-World Performance

This example shows how to initialize Web Vitals monitoring in your main application component and log the results.

// app.component.ts
import { Component, OnInit } from '@angular/core';
import { playperf } from '@playplus/core'; // Assuming helper is available
import { playlog } from '@playplus/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
})
export class AppComponent implements OnInit {
ngOnInit() {
// Start monitoring real-user performance metrics.
playperf.monitor({
onReport: (metric) => {
// Send the collected metric to our logging service.
playlog.info(`Web Vitals Metric: ${metric.name}`, {
value: metric.value,
id: metric.id
});
}
});
}
}

Additional Info

Why We Created This Helper

Web performance is critical, but its tooling is complex. Without a dedicated system, each team would need to:

  • Manually set up, configure, and maintain Lighthouse CI for every project.
  • Write custom logic to track bundle sizes.
  • Implement their own utilities for deferring scripts or monitoring Web Vitals.

This is inefficient and leads to inconsistent standards. The playperf helper automates the enforcement and simplifies the implementation of performance best practices. It provides a pre-configured safety net and easy-to-use tools so developers can build fast experiences by default.


Developer Checklist

  • Are all non-critical third-party scripts or tasks wrapped in playperf.defer()?
  • Is the code for my route being dynamically imported (code-splitting)?
  • Are all images lazy-loaded and served in modern formats (e.g., WebP) with explicit width and height attributes to prevent CLS?
  • Have I used skeleton loaders for data-heavy components instead of spinners?
  • For long lists, am I using a virtualized scrolling solution?
  • Are pure components memoized using React.memo or Angular's OnPush change detection strategy?
  • Have I analyzed the bundle size impact of any new dependencies I've added?
  • Does my feature pass the Lighthouse CI performance budget checks?

MISC

For a full view of real-world performance, we recommend integrating with:

  • Web Vitals JS Library: To send Core Web Vitals from the browser to your analytics or logging provider.
  • Sentry Performance: To track slow transactions and identify main thread stalls.
  • SpeedCurve or New Relic: For in-depth Real User Monitoring (RUM) analysis over time, across geographies and devices.

Minimum Enforcement Thresholds

All Play+ applications in the Golden Path are held to these baseline standards, which are enforced automatically in the CI/CD pipeline.

MetricTarget
LCP (Largest Contentful Paint)< 2.5s
INP (Interaction to Next Paint)< 200ms
CLS (Cumulative Layout Shift)< 0.1
JS Bundle Size (gzipped)< 250KB
Main Thread Long Tasks (>50ms)0

Lighthouse CI Configuration Example

This is a sample of the configuration used by CI to assert performance budgets. It is managed by the @playplus/perf package.

{
"ci": {
"collect": {
"numberOfRuns": 3
},
"assert": {
"assertions": {
"core-web-vitals": "error",
"largest-contentful-paint": ["error", {"maxNumericValue": 2500}],
"cumulative-layout-shift": ["error", {"maxNumericValue": 0.1}],
"interactive": ["warn", {"maxNumericValue": 3800}]
}
}
}
}