Skip to content

Instantly share code, notes, and snippets.

@rkttu
Created August 19, 2025 02:57
Show Gist options
  • Save rkttu/aa45f8509826e0081e64896a3ff3b3dd to your computer and use it in GitHub Desktop.
Save rkttu/aa45f8509826e0081e64896a3ff3b3dd to your computer and use it in GitHub Desktop.
Running .NET in the browser without Blazor and csproj
#!/usr/bin/env dotnet
#:sdk Microsoft.NET.Sdk.WebAssembly
#:property OverrideHtmlAssetPlaceholders=true
#:property AllowUnsafeBlocks=true
#:property PublishAot=false
// dotnet run Program.cs
using System.Diagnostics;
using System.Runtime.InteropServices.JavaScript;
Console.WriteLine("Hello, Browser!");
if (args.Length == 1 && args[0] == "start")
StopwatchSample.Start();
while (true)
{
StopwatchSample.Render();
await Task.Delay(1000);
}
partial class StopwatchSample
{
private static Stopwatch stopwatch = new();
public static void Start() => stopwatch.Start();
public static void Render() => SetInnerText("#time", stopwatch.Elapsed.ToString(@"mm\:ss"));
[JSImport("dom.setInnerText", "main.js")]
internal static partial void SetInnerText(string selector, string content);
[JSExport]
internal static bool Toggle()
{
if (stopwatch.IsRunning)
{
stopwatch.Stop();
return false;
}
else
{
stopwatch.Start();
return true;
}
}
[JSExport]
internal static void Reset()
{
if (stopwatch.IsRunning)
stopwatch.Restart();
else
stopwatch.Reset();
Render();
}
[JSExport]
internal static bool IsRunning() => stopwatch.IsRunning;
}
<!DOCTYPE html>
<!-- Licensed to the .NET Foundation under one or more agreements. -->
<!-- The .NET Foundation licenses this file to you under the MIT license. -->
<html>
<head>
<title>simpleweb2</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="preload" id="webassembly" />
<script type="importmap"></script>
<script type='module' src="main#[.{fingerprint}].js"></script>
</head>
<body>
<h1>Stopwatch</h1>
<p>
Time elapsed in .NET is <span id="time"><i>loading...</i></span>
</p>
<p>
<button id="pause">Pause</button>
<button id="reset">Reset</button>
</p>
</body>
</html>
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
import { dotnet } from './_framework/dotnet.js'
const { setModuleImports, getAssemblyExports, getConfig, runMain } = await dotnet
.withApplicationArguments("start")
.create();
setModuleImports('main.js', {
dom: {
setInnerText: (selector, time) => document.querySelector(selector).innerText = time
}
});
const config = getConfig();
const exports = await getAssemblyExports(config.mainAssemblyName);
document.getElementById('reset').addEventListener('click', e => {
exports.StopwatchSample.Reset();
e.preventDefault();
});
const pauseButton = document.getElementById('pause');
pauseButton.addEventListener('click', e => {
const isRunning = exports.StopwatchSample.Toggle();
pauseButton.innerText = isRunning ? 'Pause' : 'Start';
e.preventDefault();
});
// run the C# Main() method and keep the runtime process running and executing further API calls
await runMain();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment