NativeScript 9.1 Released → V8 14.9, Vite 8 HMR, built for rapid agentic visual iteration
Dig in

Experimental

The Windows platform is experimental. See Developing for Windows.

All of the Windows Runtime (Windows.*) and WinUI 3 (Microsoft.*) APIs are available to your app without any setup. On top of that, you can add:

The Windows host project ​

When you build for Windows, the CLI generates a WinUI 3 host project in platforms/windows/<ProjectName>/ and builds it with dotnet build. You don't edit the generated project directly. Instead, add MSBuild files to App_Resources/Windows, which are imported by the host project:

bash
App_Resources/
├─ Windows/
│  ├─ app.csproj            # imported by the host project
│  ├─ before-plugins.props  # imported before plugin files
│  ├─ after-plugins.props   # imported after plugin files
│  ├─ Package.appxmanifest
│  └─ Assets/
└─ ... more
  • app.csproj is the place for most customizations, such as package references and build properties. It is similar to app.gradle on Android.
  • before-plugins.props and after-plugins.props let you set properties before plugins are applied or override values set by plugins. They are similar to before-plugins.gradle on Android.

All three files are optional MSBuild fragments with a <Project> root element:

xml
<!-- App_Resources/Windows/app.csproj -->
<Project>
  <PropertyGroup>
    <ApplicationManifest Condition="'$(ApplicationManifest)' == '' and Exists('$(MSBuildThisFileDirectory)app.manifest')">$(MSBuildThisFileDirectory)app.manifest</ApplicationManifest>
  </PropertyGroup>
</Project>

Note

The whole App_Resources/Windows folder is copied into the host project, so $(MSBuildThisFileDirectory) points at the copied folder. To reference files elsewhere in your project, use $(MSBuildProjectDirectory)\..\..\..\, which resolves to your project root.

Using .NET libraries ​

The Windows runtime hosts .NET in-process, so .NET APIs can be called directly from JavaScript. The base class library is available through the System global:

ts
const stopwatch = System.Diagnostics.Stopwatch.StartNew()
// ... do some work
stopwatch.Stop()
console.log(`Took ${stopwatch.ElapsedMilliseconds}ms`)

console.log(System.Environment.MachineName)

Adding a NuGet package ​

Add a PackageReference to App_Resources/Windows/app.csproj:

xml
<Project>
  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
  </ItemGroup>
</Project>

Then register the root namespace of the library with the assembly that contains it, and use it from JavaScript:

ts
NSWinRT.dotnet.registerNamespace('Newtonsoft', 'Newtonsoft.Json')

const json = Newtonsoft.Json.JsonConvert.SerializeObject({ hello: 'world' })

registerNamespace(root, assemblyName) defines a global for the namespace root (Newtonsoft above), which resolves types from the given assembly. Assemblies are loaded from the app's output folder, including its libs and plugins subfolders.

Adding your own C# code ​

To add your own C# code, create a .NET class library targeting net10.0 (or net10.0-windows10.0.xxxxx.0 if it uses WinRT APIs) in your project, for example in native/windows/MyLibrary, and reference it from App_Resources/Windows/app.csproj:

xml
<Project>
  <ItemGroup>
    <ProjectReference Include="$(MSBuildProjectDirectory)\..\..\..\native\windows\MyLibrary\MyLibrary.csproj" />
  </ItemGroup>
</Project>
cs
// native/windows/MyLibrary/Greeter.cs
namespace MyCompany.Native;

public static class Greeter
{
    public static string Hello(string name) => $"Hello {name} from C#!";
}
ts
NSWinRT.dotnet.registerNamespace('MyCompany', 'MyLibrary')

console.log(MyCompany.Native.Greeter.Hello('NativeScript'))
// prints: Hello NativeScript from C#!

.NET tasks and delegates ​

  • Convert a returned Task to a promise with NSWinRT.toPromise(task).
  • Create a .NET delegate (for example a System.Action) with NSWinRT.dotnet.asDelegate('System.Action', fn). For WinRT delegates use NSWinRT.asDelegate instead, see Windows Marshalling › Events.
  • .NET objects are released when they are garbage collected. Call obj.release() to release one immediately.

Adding C++/WinRT components ​

Any WinRT component, for example one written in C++/WinRT, can be used from JavaScript once its metadata (.winmd) and implementation (.dll) are deployed with the app, and its classes are registered in the app manifest. @nativescript/core itself uses this approach for its NativeScript.Widgets component.

Note

Building C++/WinRT components requires Visual Studio with the Desktop development with C++ workload and the C++/WinRT extension. Build the component for every architecture you ship (x64, arm64).

1. Deploy the component ​

Place the built files in App_Resources/Windows, one folder per architecture:

bash
App_Resources/
└─ Windows/
   ├─ app.csproj
   └─ libs/
      ├─ x64/
      │  ├─ MyCompany.Native.dll
      │  └─ MyCompany.Native.winmd
      └─ arm64/
         ├─ MyCompany.Native.dll
         └─ MyCompany.Native.winmd

Copy the files matching the target architecture next to the app executable with a target in app.csproj:

xml
<Project>
  <Target Name="CopyMyCompanyNative" AfterTargets="Build">
    <ItemGroup>
      <_MyNativeFiles Include="$(MSBuildThisFileDirectory)libs\$(Platform)\*.dll;$(MSBuildThisFileDirectory)libs\$(Platform)\*.winmd" />
    </ItemGroup>
    <Copy SourceFiles="@(_MyNativeFiles)" DestinationFolder="$(OutDir)" SkipUnchangedFiles="true" />
  </Target>
</Project>

On startup, the runtime loads every .winmd file found next to the executable and in the app root.

2. Register the activatable classes ​

Add an inProcessServer extension for your classes to App_Resources/Windows/Package.appxmanifest:

xml
<Package ...>
  <!-- ... -->
  <Extensions>
    <Extension Category="windows.activatableClass.inProcessServer">
      <InProcessServer>
        <Path>MyCompany.Native.dll</Path>
        <ActivatableClass ActivatableClassId="MyCompany.Native.Greeter" ThreadingModel="both" />
      </InProcessServer>
    </Extension>
  </Extensions>
</Package>

3. Use it from JavaScript ​

The component's namespaces are available as globals, like Windows and Microsoft:

ts
const greeter = new MyCompany.Native.Greeter()
console.log(greeter.Hello('NativeScript'))

Note

When using TypeScript, you can generate typings from the component's .winmd, or declare the root namespace as any:

ts
declare const MyCompany: any

Calling Win32 DLLs ​

Functions exported from Win32 DLLs can be called without any native code using NSWinRT.win32:

ts
const kernel32 = NSWinRT.win32.define(
  'kernel32.dll',
  { GetTickCount64: [] },
  'u64',
)
console.log(kernel32.GetTickCount64())

See Windows Marshalling › Win32 functions for the supported types.

Plugins ​

Plugins provide Windows implementations with .windows.ts files. Native Windows files are placed in the plugin's platforms/windows folder:

bash
my-plugin/
├─ index.windows.ts
├─ plugin.props      # optional, imported by the host project
├─ plugin.targets    # optional, imported by the host project
└─ platforms/
   └─ windows/
      ├─ x64/
      └─ arm64/

The CLI copies the contents of platforms/windows into the host project (under plugins/<plugin-name>/) and imports the plugin's plugin.props and plugin.targets files. Use them to add package references, copy native files to the output folder or register activatable classes, the same way an app does with app.csproj. When a plugin has no plugin.props/plugin.targets, the CLI generates default ones that copy the plugin's files into plugins\<plugin-name> in the app output folder.

See @nativescript/core's plugin.targets for a complete example that deploys a C++/WinRT component and registers its classes.