Skip to content

BlazorSize knowledge base

Ed Charbeneau edited this page Apr 25, 2023 · 3 revisions

BlazorSize knowledge base

Why do I see a flash of small, medium, large, etc. content before a media query is detected?

When I load the application on mobile, I see the switching from Unmatched view to Matched up to one second. Is this a rendering performance issue?

BlazorSize will default to the Unmatched state before switching to Matched after re-rendering if the page is opened by a Small device (ex: mobile browser).

<MediaQuery Media="@Breakpoints.SmallDown">
    <Matched>
        <WeatherCards Data="forecasts"></WeatherCards>
    </Matched>
    <Unmatched>
        <WeatherGrid Data="forecasts"></WeatherGrid>
    </Unmatched>
</MediaQuery>

This can sometimes lead to unwanted content appearing for a short period of time before it is re-rendered. Also referred to as a flash of unstyled content FOUC, this phenomenon is not unique to BlazorSize or Blazor for that matter. In fact it's a common problem of modern Single Page Applications (SPA).

Why does this happen with Blazor?

This is due to the JavaScript interop and component life cycle in Blazor. Blazor has the ability to render the component in many scenarios, such as Server Pre-Rendering. In addition, components can be initialized prior to the the JavaScript interop's availability. Because BlazorSize relies on JavaScript's matchMedia API, JavaScript must be available in order to determine the browser's size. Without JavaScript a media query is simply false or unmatched until a query can be successfully made.

Why isn't a solution provided here?

BlazorSize follows the single-responsibility principle (SRP). The core responsibility of BlazorSize is to detect the browser's size, not to determine if the page and/or content is loading. Adding a solution within BlazorSize would expand the scope and add unnecessary opinion to loading patterns for Blazor.

How to fix the problem?

When using BlazorSize if you're concerned with FOUC, then components and pages responding to a media query should incorporate a loading pattern. Content should be completely hidden until the page life-cycle has completed and the content is ready for the user. A simple solution is to use a flag, or an existing library like, BlazorPro Spinkit

Using MediaQuery

<MediaQuery Media="@Breakpoints.SmallUp" @bind-Matches="IsLarge" />

<h3>Component1</h3>

@if (!isLoading)
{
    @if (IsLarge)
    {
        <div>large</div>
    }
    else
    {
        <div class="alert alert-danger">You should never see this on desktop (small)</div>
    }
}
else
{
    <span>Loading</span>
}

@code
{
    private bool isLoading = true;

    public bool IsLarge { get; set; }

    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender) isLoading = false;
    }
}

Using templates

@if (!isLoading) {
<MediaQuery Media="@Breakpoints.SmallDown">
    <Matched>
        <WeatherCards Data="forecasts"></WeatherCards>
    </Matched>
    <Unmatched>
        <WeatherGrid Data="forecasts"></WeatherGrid>
    </Unmatched>
</MediaQuery>
}
@code {
    private bool isLoading = true;
    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender) isLoading = false;
    }
}

Assigning to a Nullable type

@page "/nullable"
@if (IsSmall.HasValue)
{
    if (IsSmall.Value)
    {
        <h1>Small</h1>
    }
    else
    {
       <h1>Large</h1>
    }
} else
{
    <span>Waiting for JavaScript interop...</span>
}

<MediaQuery Media="@Breakpoints.SmallDown" MatchesChanged="@(matched => IsSmall = matched)"></MediaQuery>

@code {
    bool? IsSmall = null;
}

Accessibility/MediaTypes Media Query Support

BlazorSize supports any media query available. See Using media queries for accessibility and Media Types

For example, BlazorSize can be used to detect the user's color preference by using the prefers-color-scheme media type. In the snippet below, the media query is used to set the application's CSS file at run time, thus setting the apps theme to light or dark depending on the operating system's settings. This technique can also be used with prefers-reduced-motion` to disable animations for some users.

@inherits LayoutComponentBase
@using BlazorPro.BlazorSize

<HeadContent>
    @if (isDark && isAutoTheme)
    {
        <link href="css/bootstrap/darkly.min.css" rel="stylesheet" />
    } else
    {
        <link href="css/bootstrap/flatly.min.css" rel="stylesheet" />
    }
    <link href="css/app.css" rel="stylesheet" />
    <link href="HeadContentSample.styles.css" rel="stylesheet" />
</HeadContent>

<MediaQueryList>
    <MediaQuery Media="(prefers-color-scheme: dark)" @bind-Matches="isDark" />
    <h1>Is Contrast @isContrast</h1>
    <div class="page">
        <div class="sidebar">
            <NavMenu />
        </div>

        <main>
            <div class="top-row px-4">
               <label for="theme-auto">Auto Detect Theme</label><input type="checkbox" @bind-value="isAutoTheme" />
            </div>

            <article class="content px-4">
                @Body
            </article>
        </main>
    </div>
</MediaQueryList>

@code {
    bool isDark;
    bool isAutoTheme;
    bool isContrast;
}```

## Migration from 3.1.0 => 3.2.1 broke ResizeListener DI
### There is no registered service of type 'BlazorPro.BlazorSize.ResizeListener'.

The type `ResizeListener` now uses the interface, `IResizeListener`.
Please change the type ResizeListener in component code to the interface IResizeListener.

Example: Foo.razor
```razor
@inject IResizeListener listener