The material components MDC are pretty slick, so I want to use them in combination with blazor. Since blazor 0.3 this is possible with the ref keyword on a html tag.

First of I want to use the button. In theory you can get away with this:

mdcbutton.cshtml:

@using Microsoft.AspNetCore.Blazor
@using Microsoft.AspNetCore.Blazor.Components

<button class="mdc-button mdc-button--raised mdc-button--dense" onclick="@click">
    @Label
</button>

@functions
{
    [Parameter]
    private string Label { get; set; }
    
    [Parameter]
    private Func<Task> OnClickAsync { get; set; }

    [Parameter]
    private Action OnClick { get; set; }
    
    private async Task click()
    {
        if (OnClickAsync != null)
        {
            await OnClickAsync?.Invoke();
        }

        OnClick?.Invoke();
    }

}

And use this component like this:
<mdcbutton Title="Test title" OnClick="@MyClickHandler"></mdcbutton>

All fine, But this doesn't enable all cool material effects like the ripple for instance. To enable this, you need to use javascript interop to initiate the button.

javascript:

    Blazor.registerFunction('material.initmdcripple', function (element) {
        new mdc.ripple.MDCRipple(element);
        return true;
    });

And create a wrapper for this somewhere:

        public static void InitMdcButton(this ElementRef element)
        {
            RegisteredFunction.Invoke<bool>("material.initmdcripple", element);
        }

And use this in the component itself like this:

@using Microsoft.AspNetCore.Blazor
@using Microsoft.AspNetCore.Blazor.Components

<button class="mdc-button mdc-button--raised mdc-button--dense" onclick="@click" ref="@refbut">
    @Label
</button>

@functions
{
    [Parameter]
    private string Label { get; set; }

    private ElementRef refbut { get; set; }

    [Parameter]
    private Func<Task> OnClickAsync { get; set; }

    [Parameter]
    private Action OnClick { get; set; }

    bool firstRender { get; set; } = true;

    protected override void OnAfterRender()
    {
        if (firstRender)
        {
            firstRender = false;
            refbut.InitMdcButton();
        }
    }

    private async Task click()
    {
        if (OnClickAsync != null)
        {
            await OnClickAsync?.Invoke();
        }

        OnClick?.Invoke();
    }
}

And you have a cool ripple enabled MDC button.

Hope this helps.