{"id":14965119,"url":"https://github.com/wieslawsoltes/nxui","last_synced_at":"2026-01-28T10:01:06.546Z","repository":{"id":143726465,"uuid":"402201126","full_name":"wieslawsoltes/NXUI","owner":"wieslawsoltes","description":"NXUI (nex-ui), next-gen UI - Create minimal Avalonia applications using C# 10 and .NET 8","archived":false,"fork":false,"pushed_at":"2025-11-19T21:16:00.000Z","size":7692,"stargazers_count":229,"open_issues_count":1,"forks_count":6,"subscribers_count":6,"default_branch":"main","last_synced_at":"2026-01-14T07:53:24.861Z","etag":null,"topics":["avalonia","avaloniaui","csharp","dotnet","minimal","xaml"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wieslawsoltes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["wieslawsoltes"]}},"created_at":"2021-09-01T20:57:37.000Z","updated_at":"2026-01-11T14:01:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"90226e21-ce59-4bf8-8d36-bd93e9f2f9bb","html_url":"https://github.com/wieslawsoltes/NXUI","commit_stats":{"total_commits":644,"total_committers":4,"mean_commits":161.0,"dds":0.4503105590062112,"last_synced_commit":"57b298fea04c20826682d9c870c9d83b273b8549"},"previous_names":["wieslawsoltes/minimalavalonia"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/wieslawsoltes/NXUI","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wieslawsoltes%2FNXUI","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wieslawsoltes%2FNXUI/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wieslawsoltes%2FNXUI/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wieslawsoltes%2FNXUI/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wieslawsoltes","download_url":"https://codeload.github.com/wieslawsoltes/NXUI/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wieslawsoltes%2FNXUI/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28844011,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-28T07:39:25.367Z","status":"ssl_error","status_checked_at":"2026-01-28T07:39:24.487Z","response_time":57,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["avalonia","avaloniaui","csharp","dotnet","minimal","xaml"],"created_at":"2024-09-24T13:34:15.065Z","updated_at":"2026-01-28T10:01:06.541Z","avatar_url":"https://github.com/wieslawsoltes.png","language":"C#","funding_links":["https://github.com/sponsors/wieslawsoltes"],"categories":[],"sub_categories":[],"readme":"# NXUI (next-gen UI)\n\n[![NuGet](https://img.shields.io/nuget/v/NXUI.svg)](https://www.nuget.org/packages/NXUI)\n[![NuGet](https://img.shields.io/nuget/dt/NXUI.svg)](https://www.nuget.org/packages/NXUI)\n\nCreating minimal [Avalonia](https://avaloniaui.net/) next generation (NXUI, next-gen UI) application using C# 10 and .NET 8\n\nhttps://user-images.githubusercontent.com/2297442/132313187-32f18c4b-e894-46db-9a9d-9de02f30835e.mp4\n\n# Requisites\n\n### NXUI\n\n```xml\n\u003cPackageReference Include=\"NXUI\" Version=\"11.3.0\" /\u003e\n```\n\nAdditionally, depending on the application type:\n\n### Desktop\n\nFor Desktop extensions:\n```xml\n\u003cPackageReference Include=\"NXUI.Desktop\" Version=\"11.3.0\" /\u003e\n```\nor using plain Avalonia:\n```xml\n\u003cPackageReference Include=\"Avalonia.Desktop\" Version=\"11.3.0\" /\u003e\n```\n\n### Browser\n\n```xml\n\u003cPackageReference Include=\"Avalonia.Browser\" Version=\"11.3.0\" /\u003e\n```\n\n```\ndotnet workload install wasm-tools\n```\n\n# Usage\n\nNXUI builders now capture the full UI tree so hot reload can diff it. Return the builder\nfrom your entry point and run the app through `HotReloadHost.Run` (shipped with\n`NXUI.Desktop`) so NXUI can attach the component and reconcile live updates:\n\n```csharp\nobject Build() =\u003e\n  Window()\n    .Title(\"NXUI\")\n    .Content(Label().Content(\"NXUI\"));\n\nreturn HotReloadHost.Run(Build, \"NXUI\", args);\n```\n\n```csharp\nvar count = 0;\n\nobject Build()\n  =\u003e Window(out var window)\n    .Title(\"NXUI\").Width(400).Height(300)\n    .Content(\n      StackPanel()\n        .Children(\n          Button(out var button)\n            .Content(\"Welcome to Avalonia, please click me!\"),\n          TextBox(out var tb1)\n            .Text(\"NXUI\"),\n          TextBox()\n            .Text(window.BindTitle()),\n          Label()\n            .Content(button.ObserveOnClick().Select(_ =\u003e ++count).Select(x =\u003e $\"You clicked {x} times.\"))))\n    .Title(tb1.ObserveText().Select(x =\u003e x?.ToUpper()));\n\nreturn HotReloadHost.Run(Build, \"NXUI\", args);\n```\n\n`HotReloadHost` automatically falls back to materializing the window when hot reload is\ndisabled, so the same entry point works for Release builds without extra flags.\n\n### Running without Hot Reload\n\nIf you need to pass a concrete window to Avalonia (for example when integrating into a\ncustom lifetime), call `.Mount()` on the builder before returning it:\n\n```csharp\nWindow Build()\n  =\u003e Window()\n    .Title(\"NXUI\")\n    .Content(Label().Content(\"NXUI\"))\n    .Mount();\n\nAppBuilder.Configure\u003cApplication\u003e()\n  .UsePlatformDetect()\n  .UseFluentTheme()\n  .StartWithClassicDesktopLifetime(Build, args);\n```\n\nMinimalistic Desktop app:\n```csharp\nreturn HotReloadHost.Run(\n  () =\u003e Window().Content(Label().Content(\"NXUI\")), \n  \"NXUI\", \n  args, \n  ThemeVariant.Dark);\n```\n\n# Generate\n\nC#\n```bash\ncd src/Generator\ndotnet run -- ../NXUI/Generated\n```\n\nF#\n```bash\ncd src/Generator\ndotnet run -- ../NXUI.FSharp/Generated -fsharp\n```\n\n# dotnet run app.cs\n\nUsing .NET 10 you can run GUI apps using scripts: https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/#using-shebang-lines-for-shell-scripts\n\nNote: You might need to adjust shebang line to `#!/usr/bin/dotnet run`\n\nApp.cs\n```csharp\n#!/usr/local/share/dotnet/dotnet run\n#:package NXUI.Desktop@11.3.0\nusing Avalonia.Controls;\nusing Avalonia.Styling;\nusing NXUI.HotReload;\nusing static NXUI.Builders;\n\nreturn HotReloadHost.Run(\n    () =\u003e Window().Content(Label().Content(\"NXUI\")),\n    \"NXUI\",\n    args,\n    ThemeVariant.Dark,\n    ShutdownMode.OnLastWindowClose);\n```\n\n```bash\nchmod +x App.cs\n./App.cs\n```\n\n![image](https://github.com/user-attachments/assets/33f7f915-13a2-4c45-b9e3-ecd5dfdfd353)\n\nMore complex app:\n\n```csharp\n#!/usr/local/share/dotnet/dotnet run\n#:package NXUI.Desktop@11.3.0\nusing NXUI.HotReload;\nusing static NXUI.Builders;\n\nvar count = 0;\n\nobject Build()\n    =\u003e Window(out var window)\n        .Title(\"NXUI\").Width(400).Height(300)\n        .Content(\n            StackPanel()\n                .Children(\n                    Button(out var button)\n                        .Content(\"Welcome to Avalonia, please click me!\"),\n                    TextBox(out var tb1)\n                        .Text(\"NXUI\"),\n                    TextBox()\n                        .Text(window.BindTitle()),\n                    Label()\n                        .Content(button.ObserveOnClick().Select(_ =\u003e ++count).Select(x =\u003e $\"You clicked {x} times.\"))))\n        .Title(tb1.ObserveText().Select(x =\u003e x?.ToUpper()));\n\nreturn HotReloadHost.Run(Build, \"NXUI\", args);\n```\n\n![image](https://github.com/user-attachments/assets/6dfea182-9725-4904-a201-b9c48aea2915)\n\n## F# Support\n\nFrom F# 9.0 and above the compiler resolves [extension methods instead of instrinsic properties](https://github.com/dotnet/fsharp/pull/16032) so, there's no need for a separate F# package or any additional changes to your project files.\n\nExtension methods provided by the main package `NXUI`\n\n```fsharp\nopen Avalonia\nopen Avalonia.Controls\n\nopen NXUI.Extensions\nopen NXUI.HotReload\nopen type NXUI.Builders\n\nlet Build () : obj =\n    let mutable count = 0\n    let mutable window = Unchecked.defaultof\u003cWindow\u003e\n    let mutable button = Unchecked.defaultof\u003cButton\u003e\n    let mutable tb1 = Unchecked.defaultof\u003cTextBox\u003e\n\n    Window(window)\n        .Title(\"NXUI\")\n        .Width(400)\n        .Height(300)\n        .Content(\n            StackPanel()\n                .Children(\n                    Button(button).Content(\"Welcome to Avalonia, please click me!\"),\n                    TextBox(tb1).Text(\"NXUI\"),\n                    TextBox().Text(window.BindTitle()),\n                    Label()\n                        .Content(\n                            button.ObserveOnClick()\n                            |\u003e Observable.map (fun _ -\u003e\n                                count \u003c- count + 1\n                                count)\n                            |\u003e Observable.map (fun x -\u003e $\"You clicked {x} times.\")\n                            |\u003e _.ToBinding()\n                        )\n                )\n        )\n        .Title(tb1.ObserveText())\n    |\u003e box\n\n[\u003cEntryPoint\u003e]\nlet Main argv = HotReloadHost.Run(Build, \"NXUI\", argv)\n```\n\n\u003e ### F# 8.0 Support\n\u003e\n\u003e The compiler feature is available in the .NET9 SDK and above so even if you target a lower dotnet version you don't need to change your project files.\n\u003e\n\u003e However, if you must to use the .NET8 SDK you only need to set the language version to preview\n\u003e In your \\*.fsproj project and you'll get the same benefits.\n\u003e\n\u003e ```xml\n\u003e \u003cPropertyGroup\u003e\n\u003e     \u003cTargetFramework\u003enet8.0\u003c/TargetFramework\u003e\n\u003e     \u003cLangVersion\u003epreview\u003c/LangVersion\u003e\n\u003e \u003c/PropertyGroup\u003e\n\u003e ```\n\n## Extensions\n\nNXUI ships with a rich set of extension methods and builder helpers so that all\nUI composition can be expressed in C#.  The code generator produces most of\nthese members for every Avalonia control and property.\n\n### Builders\n\n`NXUI.Builders` exposes factory methods for every control type.  Each method\ncreates the control instance and overloads let you capture it via `out var` for\nlater use.\n\n### Property helpers\n\nFor each Avalonia property the following methods are generated:\n\n* **`\u003cName\u003e(value)`** – set the property value.\n* **`\u003cName\u003e(IBinding, mode, priority)`** – bind with an Avalonia binding.\n* **`\u003cName\u003e(IObservable\u003cT\u003e, mode, priority)`** – bind from an observable.\n* **`Bind\u003cName\u003e(mode, priority)`** – create a binding descriptor.\n* **`Observe\u003cName\u003e()`** – observable of property values.\n* **`On\u003cName\u003e(handler)`** – pass the observable to a handler.\n* **`ObserveBinding\u003cName\u003e()`** – observe binding values including errors.\n* **`OnBinding\u003cName\u003e(handler)`** – receive the binding observable.\n* **`Observe\u003cName\u003eChanged()`** – observe full change events.\n* **`On\u003cName\u003eChanged(handler)`** – handler for change observable.\n\nEnum properties get convenience methods for each enum value, e.g.\n`HorizontalAlignmentCenter()`.\n\n### Event helpers\n\nFor routed and CLR events:\n\n* **`ObserveOn\u003cEventName\u003e(routes)`** – returns an `IObservable` sequence.\n* **`On\u003cEventName\u003e(handler, routes)`** – handler receiving the observable.\n* **`On\u003cEventName\u003eHandler(action, routes)`** – attach a simple callback.\n\n### Style setters\n\n`Set\u003cClassName\u003e\u003cPropertyName\u003e` methods on `Style` and `KeyFrame` let you define\nstyle values using constants, bindings or observables.\n\n### Core runtime helpers\n\n`NXUI.Extensions.AvaloniaObjectExtensions` provides `BindOneWay` and\n`BindTwoWay` to link properties or observables without verbose binding code.\n\n`NXUI.Extensions.ReactiveObservableExtensions` adds utilities for reactive\nworkflows:\n\n- `ObserveOnUiThread` / `SubscribeOnUiThread`\n- `TakeUntilDetachedFromVisualTree` / `SubscribeUntilDetached`\n- `DisposeWith`\n- `DataTemplate\u003cT\u003e`\n- `WhenAnyValue` (single or multiple expressions)\n\nTogether these extensions enable complex, reactive UIs built entirely in code\nwhile managing resources with minimal overhead.\n\n## Hot Reload\n\n### Using NXUI Hot Reload\n\n1. **Hot reload is always on** – every NXUI project now emits builder-based controls and ships the hot reload runtime by default. No MSBuild properties or preprocessor symbols are required.\n2. **Run through the host** – wrap your entry point with `HotReloadHost.Run` so NXUI can register the component and reconcile live instances:\n\n   ```csharp\n   using NXUI.HotReload;\n\n   object Build() =\u003e\n       Window()\n           .Title(\"NXUI Hot Reload\")\n           .Content(Label().Content(\"Edit a file and save to trigger hot reload.\"));\n\n   return HotReloadHost.Run(Build, \"SampleApp\", args);\n   ```\n\n3. **Use `dotnet watch` or IDE hot reload** – the runtime listens for metadata updates and calls `NodeRenderer.Reconcile` with the previous node snapshot. The window stays mounted and control state (e.g., `TextBox.Text`) survives.\n4. **Turn on diagnostics when needed** – set `NXUI_HOTRELOAD_DIAGNOSTICS=1` to see per-reconciliation summaries, including counts for property sets, child add/remove/move operations, and replacements.\n5. **Implicit boundaries (optional)** – set `\u003cEnableNXUIHotReloadBoundaries\u003etrue\u003c/EnableNXUIHotReloadBoundaries\u003e` to weave `[HotReloadBoundary]` onto controls listed in `build/HotReloadBoundaries.json`. Samples do this automatically for Debug builds via `samples/Directory.Build.props`. Use `dotnet run --project src/NXUI.Cli -- hotreload boundaries --manifest build/HotReloadBoundaries.json --assembly path/to/MyApp.dll` to inspect which controls were annotated (manifest hits, explicit attributes, or state-adapter skips). The same Fody pass also injects `[assembly: MetadataUpdateHandler(typeof(NXUI.HotReload.HotReloadMetadataUpdateHandler))]`, so IDE / `dotnet watch` notifications reach NXUI without manual attributes.\n\n### Troubleshooting\n\n- **No updates apply** – ensure your builder delegate returns an `ElementNode` tree (not `.Mount()`ed controls) and that it is hosted by `HotReloadHost.Run`.\n- **State resets on list changes** – provide explicit `.Key(\"stable-id\")` for repeating builders or wrap complex containers with `.HotReloadBoundary()` so the diff engine can reason about reuse. See `docs/hot-reload-best-practices.md` for patterns.\n- **Bindings/events stop firing** – check analyzer warnings (see `NXUI.Analyzers`) and confirm you are not instantiating Avalonia controls manually; the builder pipeline must remain in control for hot reload to succeed.\n- **Layout thrash during updates** – enable diagnostics to ensure excessive replacements are not happening; large replace counts often mean missing keys or boundaries.\n\n### Release Builds\n\n- The hot reload runtime now ships in every build configuration. `NXUI.props` injects the required `RuntimeHostConfigurationOption` automatically so metadata updates flow without extra project tweaks.\n- When trimming, keep `NXUI.HotReload.*` rooted (the shipped linker descriptors already do this) if you want hot reload support in the published app.\n\n### Further Reading\n\n- `docs/hot-reload-architecture.md` – end-to-end design.\n- `docs/hot-reload-implementation-plan.md` – milestone tracking.\n- `docs/hot-reload-best-practices.md` – guidance for `Key()` and `HotReloadBoundary()` usage.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwieslawsoltes%2Fnxui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwieslawsoltes%2Fnxui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwieslawsoltes%2Fnxui/lists"}