Monitoring Frontend Errors in the Hyvä Theme
AI generated
Hyvä
phtml
Hyvä Theme · Testing & CI
Monitoring Frontend Errors in the Hyvä Theme
Why the Magento exception log never sees what actually goes wrong in a customer's browser

A customer whose click on Add to Cart silently fails because of a broken Alpine expression leaves no trace whatsoever in the Magento exception log, since the error happens entirely inside the browser. This article shows how to bring JavaScript error tracking with Sentry into a Hyvä theme in a CSP-compliant way, which error sources arise specifically from failed Alpine initialization, and how this layer is cleanly separated from pure server-side logging.

9 min read Sentry CSP Error Tracking

1. Why server-side Magento logging fundamentally doesn't cover frontend errors

The Magento exception.log and its associated report classes only log errors that occur inside PHP execution on the server, such as a failed database query or invalid input in a controller. An error that only arises after the HTML has been delivered to the browser, for example because an Alpine expression accesses a property that doesn't exist, simply never reaches this layer, because there is no HTTP request left between the server and that error.

For a theme whose interactivity runs almost entirely through client-side Alpine.js, this gap means a substantial part of the actual user experience goes completely unobserved as long as a team relies exclusively on server-side logging. A broken mini-cart button can persist for weeks without a single signal showing up in an existing monitoring setup, because from the server's point of view everything worked exactly as expected.

2. Integrating Sentry into a Hyvä theme in a CSP-compliant way

A theme built on the CSP variant parent hyva-themes/magento2-default-theme-csp needs an explicit allowance in the content security policy for every additional script and every additional network endpoint, instead of being able to rely on an open configuration. Magento's own CSP module allows this declaratively through csp_whitelist.xml, so both the Sentry loader domain for script-src and the ingest endpoint for connect-src get registered without touching the central response header directly.

Sentry's own initialization script follows the same rule as any other inline block in the theme: it must be registered through hyvaCsp->registerInlineScript() so the matching nonce gets set correctly and the script isn't blocked by the theme's own strict policy. Skip that step and the browser console reports a CSP violation before a single real error has ever been tracked.


<!-- app/code/Mironsoft/Monitoring/etc/csp_whitelist.xml -->
<csp_whitelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Csp:etc/csp_whitelist.xsd">
    <policies>
        <policy id="script-src">
            <values>
                <value id="sentry-loader" type="host">js.sentry-cdn.com</value>
            </values>
        </policy>
        <policy id="connect-src">
            <values>
                <value id="sentry-ingest" type="host">*.ingest.sentry.io</value>
            </values>
        </policy>
    </policies>
</csp_whitelist>

3. Capturing failed x-data initialization as its own error source

Alpine catches errors inside an expression internally in its own try-catch block and only writes them to the browser console through console.error, instead of throwing them as a genuine, unhandled exception. That matters for a monitoring setup, because Sentry's default integration through window.onerror and unhandledrejection systematically misses this entire class of error, even though the expression genuinely failed and the affected component remains functionally broken.

Two approaches close this gap reliably: either every component factory registered through Alpine.data() gets wrapped in its own try-catch block that explicitly reports an error through Sentry.captureException() with the component name as a tag, or Sentry's console integration gets configured deliberately to capture console.error calls as error events, without getting flooded by plain debug logging in the process.


// Mironsoft_Monitoring/web/js/alpine-error-tracking.js
document.addEventListener('alpine:init', () => {
  const originalData = window.Alpine.data;

  window.Alpine.data = function (name, callback) {
    return originalData(name, (...args) => {
      try {
        return callback(...args);
      } catch (error) {
        window.Sentry?.captureException(error, {
          tags: { alpineComponent: name },
        });
        throw error;
      }
    });
  };
});

4. How this differs from pure server-side error logging

Frontend monitoring doesn't replace server-side exception.log, it adds a layer that looks at the same application from a completely different angle: while the server log shows what failed on the PHP side for a specific request, Sentry in the browser shows what went wrong after the HTML was delivered, in a customer's actual interaction, regardless of whether another request was ever sent to the server at all.

Both layers should therefore coexist, and ideally be correlated through a shared request ID written both into the server log and as a tag on every Sentry event at the initial page load. That makes it possible to reconstruct afterward whether a frontend error was a consequence of an earlier server problem, such as an incomplete GraphQL response, or a genuinely standalone client-side issue.

5. Enriching Sentry events with store view and page context

An error with no context, showing nothing but a stack trace line in a minified JavaScript file, is of little use for debugging as long as it's unclear which store view, which customer group and which page type it actually occurred on. A small, layout-driven block writes this information into a global window.__pageContext__ object right when the page renders, which Sentry's initialization then picks up as additional tags.

This enrichment turns an anonymous error report into an actually analyzable data source: if errors cluster noticeably on the English store view, or exclusively within a specific customer group with different pricing rules, that gives a concrete starting point for further investigation, instead of just an anonymous error count with no relation to the affected user segment at all.


Sentry.init({
  dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0',
  beforeSend(event) {
    const ctx = window.__pageContext__ || {};
    event.tags = {
      ...event.tags,
      storeView: ctx.storeView,
      customerGroup: ctx.customerGroup,
      pageType: ctx.pageType,
    };
    return event;
  },
});

6. Filtering out noise from bots, extensions and third-party scripts

Without deliberate filtering, a significant share of incoming Sentry events comes not from the actual theme code at all, but from browser extensions that inject their own JavaScript into every visited page, or from automated crawlers that operate forms in unusual ways and trigger errors no real customer would ever encounter. These errors dilute the actual signal and make it harder to catch genuine regressions in your own code in time.

Sentry's denyUrls option filters out stack traces whose source is recognizably outside the theme's own domain, such as chrome-extension:// or moz-extension://, while ignoreErrors suppresses known, harmless error messages like ResizeObserver loop limit exceeded across the board. Both filters should be scoped deliberately narrow and reviewed regularly, so they never accidentally swallow real, relevant errors too.

7. Deliberately separating error tracking from pure performance monitoring

Sentry and comparable tools often bundle performance monitoring, sampling load times and interaction metrics, alongside pure error tracking, pursuing a completely different goal and typically generating a far higher event rate. If both get enabled unfiltered at the same sampling rate, a Sentry plan's quota can be exhausted by performance traces within a few days, while the actually relevant error events get dropped by sampling instead.

For a Hyvä theme whose primary monitoring goal is initially functional frontend errors rather than pure performance metrics, a low or initially disabled sampling rate for performance traces pays off, while error events stay captured at a full, unsampled one hundred percent, so not a single genuine problem gets lost.

8. Practical example: tracing a failed Alpine component end to end

A concrete example: a customer opens the product page of a configurable item whose Alpine component initConfigurableOptions fails when it accesses a selectedOption.sku property that doesn't exist for this particular product variant. Without the wrapping described above, this error would stay visible only in the customer's own browser console, while the team assumes the page works fine, since the server responded successfully with a status of 200.

With active Alpine error wrapping and enriched page context, a Sentry event appears instead, tagged with alpineComponent set to initConfigurableOptions, the affected product SKU in the extra context, and the store view the error occurred on. The team can now reproduce the error deliberately, instead of only learning about the problem through a random customer complaint.

9. Cleanly separating Sentry between local development and production

Without a clear separation by environment, errors from a local Docker development setup land in the same Sentry project as genuine production errors, which quickly makes the error list useless, since a test error deliberately triggered during development gets the same priority as a real customer issue. An environment field in the Sentry configuration, derived from Magento's store view configuration, solves this by making events cleanly filterable across development, staging and production.

For local development, a significantly reduced or fully disabled Sentry integration pays off too, so local debugging sessions with deliberately provoked errors don't eat into the production Sentry plan's quota and don't needlessly dilute the team's overview.


Sentry.init({
  dsn: window.__pageContext__?.sentryDsn,
  environment: window.__pageContext__?.environment || 'production',
  tracesSampleRate: window.__pageContext__?.environment === 'production' ? 0.1 : 0,
});
Layer Sees Frontend JS Errors Sees Server Errors Typical Tool
Magento exception.log No Yes Built-in Magento logging
Sentry Browser SDK Yes No Sentry, Bugsnag, Rollbar
Alpine error wrapping Yes, specifically for x-data errors No Custom Alpine.data wrapper
Server APM No Yes, including performance New Relic, Sentry Performance
Access log analysis No Partially, HTTP layer only GoAccess, log aggregation

Mironsoft

Hyvä theme development and Luma migration

Still running Luma, or a Hyvä theme that just doesn't feel right?

We build Hyvä themes for Magento from scratch or migrate existing Luma shops cleanly, with Tailwind CSS, Alpine.js, and none of the unnecessary JavaScript baggage.

Luma-to-Hyvä Migration

Move an existing shop to Hyvä in a structured way, without losing functionality.

Custom Theme Development

Build a custom Hyvä theme from scratch based on your design.

Performance Optimization

Improve Core Web Vitals and load times in the Hyvä frontend with purpose.

10. Summary

Frontend Error Monitoring in the Hyvä Theme: Key Takeaways

Separate error worlds

Server logging and frontend monitoring fundamentally cover different classes of errors.

CSP-compliant integration

csp_whitelist.xml and registerInlineScript() keep Sentry fully compliant with the theme's own policy.

Alpine-specific wrapping

A custom Alpine.data wrapper catches errors Alpine internally only writes to console.error.

Context over anonymity

Store view, customer group and page type as tags make error events actually analyzable.

11. FAQ: Frontend Error Monitoring in the Hyvä Theme: Key Takeaways

1Why doesn't the Magento exception.log see frontend JavaScript errors?
Because it only logs errors that occur inside PHP execution on the server. An error that only arises after the HTML has been delivered to the browser never reaches this layer, since no further HTTP request is involved.
2How is Sentry integrated into a Hyvä theme in a CSP-compliant way?
Through a csp_whitelist.xml that allows the Sentry loader domain for script-src and the ingest endpoint for connect-src, plus hyvaCsp->registerInlineScript() for the initialization script itself.
3Why does Sentry's default integration miss failed Alpine expressions?
Because Alpine catches errors inside an expression internally in its own try-catch block and only logs them through console.error, instead of throwing them as an unhandled exception that window.onerror could catch.
4How can failed x-data initializations still be captured?
By wrapping every component factory registered through Alpine.data() in its own try-catch block that explicitly reports an error through Sentry.captureException() with the component name as a tag.
5How does frontend monitoring differ from server-side error logging?
Server logging shows what failed on the PHP side for a specific request. Frontend monitoring shows what went wrong after the HTML was delivered, in a customer's actual interaction, regardless of any further server requests.
6Why should Sentry events be enriched with store view and page context?
Because an anonymous error report with no context is hard to act on. Only with tags like store view, customer group and page type does it become clear whether errors cluster around a specific user segment.
7How are browser extensions and bot traffic filtered out of monitoring?
Through Sentry's denyUrls option, which filters out stack traces with a recognizably external source like chrome-extension://, and through ignoreErrors for known, harmless error messages.
8Why should performance monitoring be considered separately from error tracking?
Because performance traces can generate a far higher event rate and, at the same unfiltered sampling rate, can exhaust a Sentry plan's quota while relevant error events get dropped instead.
9How are local development errors separated from real production errors?
Through an environment field in the Sentry configuration, derived from Magento's store view configuration, which makes events cleanly filterable across development, staging and production.
10What does the practical example with initConfigurableOptions actually show?
How a missing property access inside an Alpine component stays completely invisible without error wrapping, while with active wrapping and enriched context it appears as a concrete, reproducible Sentry event with product SKU and store view.