The latest version of React Native for Windows has changed the namespace in which the IReactPackageProvider interface has been implemented. The previous one was:
Microsoft.ReactNative.Bridge;
while the new one is:
Microsoft.ReactNative;
The snippets in the post have been updated to reflect this change.
React Native offers a wide range of modules to extend the core functionalities of the platform, developed either by the community or by the React team himself. These modules range from UI controls, helpers and services. Some of them are "generic" and they perform tasks which don't require access to any specific platform feature. For example,Reduxis a very popular state management library used in React applications but since, by default, everything is stored in memory, it basically runs everywhere. In fact, the same library can be used either with web applications (using React) or native applications (using React Native).
However, there are many scenarios in the native development space where the only way to achieve a task is by interacting with the native APIs offered by the platform. Saving a file in the local storage; getting information about the device; accessing to the GPS are just a few examples. In this case, the module can't be generic, because each platform has its own way to access to native features. This is why React Native supports the concept ofnative modules: a module that is leveraged in the main application through JavaScript but that, under the hood, invokes a native component specific for the platform where the application is running. Being native, each component isn't built in JavaScript, but using the native language used by the platform. For example, take a look at the popular moduleAsyncStorage, which can be used to read and write files in the local storage. As you can see, we have a folder calledios, which contains a XCode project; theandroidfolder, instead, contains some Java files. Both projects simply expose the same features, but implemented using the specific language and APIs from iOS and Android.
If you have some experience with Xamarin and Xamarin Forms, this approach will be familiar. It's the same exact concept, in fact, behind Xamarin Plugins: the entry point is a C# wrapper which, under the hood, calls Swift, Java or UWP code based on the platform where the application is running.
In this blog post we're going to learn how we can build such a native module with Windows support, so that we can access to native Windows APIs when our React Native application is running on Windows.
Create a React Native for Windows project
As first step, let's create a React Native project with Windows support. I won't explain all the steps, since they are very well documentedon the official GitHub repository. Additionally, I already introduced React Native and the recently added Windows supportin another article.
Once you have completed all the steps, open thewindowsfolder inside the project and double click on the Visual Studio solution. As we have learnedin the previous article, the solution contains a single application which is the host for our React Native content. In a regular scenario you won't need to touch the code here, since the whole development will happen in the JavaScript world. However, in our case we have to make an exception, since we need to interact with native Windows APIs to build our module.
Create the module with a Windows Runtime Component
Let's start by adding a new project to the solution, which type is Windows Runtime Component. The component will give us access to all the Universal Windows Platform APIs. Additionally, being a WinRT component, it can be consumed regardless of the language used to build the main application. As a consequence, you're free to create a C# or C++ component, based on the language you're most familiar with. We'll be able to leverage it from the main React Native host (which is built in C++) regardless.
In my case, C# is my favorite language, so I've chosen theWindows Runtime Component (C#)template. If you want to use C++, instead, you need to choose theWindows Runtime Component (C++/WinRT)template. If you don't see it, you can install it usingthis extension. Pay attention to not use the C++/CX template, because this approach to build C++ components for the Universal Windows Platform has been deprecated.
Once the project has been created, you need to reference a couple of projects which are already included as part of the React Native for Windows solution. Right click on the project you have just created and chooseAdd → Reference.
From theProjectssection, chooseMicrosoft.ReactNative. This is the core React Native implementation for Windows.
From theShared Projectssection, chooseMicrosoft.ReactNative.SharedManaged. This project will give us access to C# helpers that will make easy to build a native module. If you're using C++, the project to reference is calledMicrosoft.ReactNative.Cxx.
These projects are very important because they define many attributes which will allow you to export the native code to JavaScript. This way, the developers who are going to use your module in their React Native projects don't need to know anything about C++, C# or Windows 10 APIs. They just need to use standard and plain JavaScript code.
Before starting to write some code, expand theReferencessection of the project, select theMicrosoft.ReactNativelibrary, right click on it and chooseProperties. SetCopy Localtofalse, to make sure that there are no conflicts with the main application.
Now we can start building our module. We're going to create a simple one to expose the information about the model of the device where the application is running. Let's start by renaming the default class included in the project with a more meaningful name, likeSampleComponent. This should trigger the renaming also of the class declared inside the code.
Now we just need to define one or more methods, that will be exposed to the React Native application. In our case, we're going to define a single method that will return the current device name, using theEasClientDeviceInformationclass included in the Universal Windows Platform:
using Windows.Security.ExchangeActiveSyncProvisioning;
namespaceSampleReactModule
{
classSampleComponent
{
publicstringGetDeviceModel()
{
EasClientDeviceInformation info = new EasClientDeviceInformation();
return info.SystemProductName;
}
}
}
The next step is to leverage the attributes provided by theMicrosoft.ReactNativelibrary to export the class and the method we have created to React Native. In our scenario we're going to use two of the many available ones:
[ReactModule], which we're using to decorate the whole class.
[ReactMethod], which we're using to decorate every single method we want to expose.
This is the final look of our class:
using Microsoft.ReactNative.Managed;
using Windows.Security.ExchangeActiveSyncProvisioning;
namespaceSampleReactModule
{
[ReactModule]
classSampleComponent
{
[ReactMethod("getDeviceModel")]
publicstringGetDeviceModel()
{
EasClientDeviceInformation info = new EasClientDeviceInformation();
return info.SystemProductName;
}
}
}
Notice that we have passed thegetDeviceModelvalue as parameter of theReactMethodattribute. This is how we define the name of the method that will be available through JavaScript to the React Native project.
There are many other attributes, which can be used to expose additional stuff to the React Native application, like properties or event handlers. They are all documentedhere.
The next thing we need is aReactPackageProvider, which is a special object that will allow the React Native project to load all the modules exposed by our library. By default, in fact, a React Native application loads only the built-in modules. Right click on the project and chooseAdd → Class. Name itReactPackageProvider. The class is very simple:
using Microsoft.ReactNative;
using Microsoft.ReactNative.Managed;
namespaceSampleReactModule
{
publicsealedclassReactPackageProvider: IReactPackageProvider
{
publicvoidCreatePackage(IReactPackageBuilder packageBuilder)
{
packageBuilder.AddAttributedModules();
}
}
}
It just needs to implement theIReactPackageProviderinterface, which will require you to implement theCreatePackage()method. Inside it we leverage thepackageBuilderobject and theAddAttributedModules()method to load all the modules we have decorated with the[ReactModule]attribute inside our project.
Register the module in the React Native application
As already mentioned, by default React Native for Windows registers only the modules which are included in the main project. If we would have created theSampleComponentclass inside the main React Native project, we wouldn't have needed any additional step. However, in our case we are using a separate Windows Runtime Component, which allows us to leverage a different language (C#), so we need an extra step to register the library in the main application.
First, right click on the React Native for Windows project and chooseAdd → Reference. Select from the list the name of the Windows Runtime Component you have just created (in my sample, it'sSampleReactModule) and press Ok.
Expand theApp.xamlnode in the main React Native project and double click on theApp.cppfile. Inside theAppconstructor you will find the following entry:
SampleReactModuleis the name of the Windows Runtime Component we have previously created, thus it's the namespace that contains ourReactPackageProviderimplementation. To make this code compiling we need also to include the header file of our module at the top:
#include"winrt/SampleReactModule.h"
This is how the wholeApp.cppfile should look like:
#include"pch.h"#include"App.h"#include"ReactPackageProvider.h"#include"winrt/SampleReactModule.h"usingnamespace winrt::NativeModuleSample;
usingnamespace winrt::NativeModuleSample::implementation;
/// <summary>/// Initializes the singleton application object. This is the first line of/// authored code executed, and as such is the logical equivalent of main() or/// WinMain()./// </summary>
App::App() noexcept
{
MainComponentName(L"NativeModuleSample");
#if BUNDLE
JavaScriptBundleFile(L"index.windows");
InstanceSettings().UseWebDebugger(false);
InstanceSettings().UseLiveReload(false);
#else
JavaScriptMainModuleName(L"index");
InstanceSettings().UseWebDebugger(true);
InstanceSettings().UseLiveReload(true);
#endif#if _DEBUG
InstanceSettings().EnableDeveloperMenu(true);
#else
InstanceSettings().EnableDeveloperMenu(false);
#endif
PackageProviders().Append(make<ReactPackageProvider>()); // Includes all modules in this project
PackageProviders().Append(winrt::SampleReactModule::ReactPackageProvider());
InitializeComponent();
// This works around a cpp/winrt bug with composable/aggregable types tracked// by 22116519
AddRef();
m_inner.as<::IUnknown>()->Release();
}
Using the module from JavaScript
That's it! Now we can move to the React Native project to start using the module we have just built from JavaScript. Open the folder which contains your React Native project with your favorite web editor. For me, it's Visual Studio Code. For simplicity, I've implemented the component which uses the native module directly in theApp.jsfile of my React Native project, so that it will be the startup page.
The first step is importing theNativeModuleobject exposed by React Native in our component:
import { NativeModules } from'react-native';
Our module will be exposed through this object, by leveraging the name of the class we have decorated with the[ReactModule]attribute. In our case, it'sSampleComponent, so we can use the following code to access to theGetDeviceModule()method:
NativeModules.SampleComponent.getDeviceModel();
Notice that we can reference the method with the lowercase name,getDeviceModel(), thanks to the parameter we have passed to the[ReactMethod]attribute. However, by default the methods exposed by native modules are implemented in an asynchronous way using callbacks. As such, if we want to consume thegetDeviceModel()method, we need to use the following approach:
getModel = () => {
var current = this;
NativeModules.SampleComponent.getDeviceModel(function(result) {
current.setState({model: result});
})
}
The method accepts a function, that is invoked once the asynchronous operation is completed. Inside this function we receive, as parameter, the result returned by our native method (in our case, the model of the device). In our sample, we store it inside the component's state, so that we can display it in the user interface using the JSX syntax:
<Text>Model: {this.state.model}</Text>
Before calling thesetState()method, however, we need to save a reference to the main context. Inside the callback, in fact, we have moved to a different context, so we don't have access to the helpers exposed by React Native.
Below you can find the full definition of our component:
If you're familiar with React Native, the code should be easy to understand:
We have defined a function calledgetModel(), which takes care of interacting with our native module. The model of the device is stored inside the component's state, using thesetState()function provided by React Native.
Therender()methods defines the UI component, using the JSX syntax. The UI is very simple:
We have a<Button>, which invokes thegetModel()function by leveraging theonPressevent.
We have a<Text>, which displays the value of themodelproperty stored inside the state.
Testing the code
Now it's time to test the code! First, right click on the React Native project in Visual Studio and chooseDeploy. Before doing it, however, use theConfiguration Managerdropdown to make sure you're targeting the right CPU architecture. By default, in fact, the solution will target ARM, so you will have to switch to x86 or x64. If it's the first time you build it, it will take a while since C++ compilation isn't exactly fast 😃 Once it's done, open a command prompt on the folder which contains your React Native project and typeyarn start. This will launch the Metro packager, which will serve all the React Native components to your application. Once the dependency graph has been loaded, you can open the Start menu and launch the Windows application you have just deployed from Visual Studio. If you have done everything in the correct way, you will see the very simple UI we have built in our component. By pressing the button you will invoke the native module and you will see the model of your device being displayed:
Improving the code
If you compare this development experience with the one you have when you add a 3rd party module to your project using yarn or npm, you realize that it's quite different. In such a case, in fact, you don't have to deal with theNativeModulesobject; or you don't need to use callbacks to call the various methods. Let's use again the example of the popularAsyncStoragemodule. If you want to store some data in the storage, you just import a reference to theAsyncStorageobject and then you call an asynchronous method calledsetItem:
Can we achieve the same goal with the native module we have just built? The answer is yes! We just need to build a JavaScript wrapper, that will make consuming our native module more straightforward.
In Visual Studio Code add a new file in your React Native project and call itSampleComponent.js. First, let's import the sameNativeModulesobject we have previously imported in the main component:
import { NativeModules } from'react-native';
Now, usingJavaScript Promises, we can build a wrapper to thegetResult()method. Thanks to Promises, we can enable an easier approach to consume our asynchronous API, thanks to theasyncandawaitkeywords. If you have some C# background, this approach is similar to use theTaskCompletionSourceclass to build asynchronous operations that returns aTaskobject-
The method returns a newPromise, which requires us to fulfill two objects:
resolve, which is invoked when the operation has completed successfully.
reject, which is invoked when the operation has failed.
Inside the Promise we invoke thegetDeviceModel()method, in the same way we were doing before in the component. The only difference is that, in this case, once the callback is completed we pass theresultto theresolve()method in case of success; otherwise, we pass theerrorto thereject()method.
Now that we have built our wrapper, we can simplify the component we have previously built. First, remove the following line:
import { NativeModules } from'react-native';
Then replace it with the following one:
import * as SampleComponent from'./SampleComponent'
This import will expose the functions we have defined in our wrapper through theSampleComponentobject. Now you can change thegetModel()function simply with this code:
getModel = async () => {
var model = await SampleComponent.getDeviceModel();
this.setState( { model: model});
}
As you can see, thanks to Promises and theasyncandawaitkeywords, the code is simpler to write and read. We just need to mark the function asasync, then call theSampleComponent.getDeviceModel()function with theawaitprefix. Since the asynchronous operation is managed for us, we can treat it like if it's synchronous and just store in a variable the result, which will contain the device model. Since we aren't using callbacks, we can also set the state directly by callingthis.setState(). If you are a C# developer, everything should be very familiar, since it's the same async and await approach supported by C#.
That's it! Now launch the application again. In this case we didn't touch the native project, so if the Metro packager was still running, we should already be seeing the updated version. Of course there won't be any difference, since we didn't change the UI or the behavior, but the code is easier to read and maintain now.
Exporting the module
So far, we have added the native module implementation directly in the project. What if we want to create a true self-contained module, that we can install using yarn or npm like any other 3rd party module? Well, unfortunately the story isn't complete for Windows yet. By following the documentationavailable on GitHubit's easy to create the skeleton of the module. We just need to create a new React Native module project, using thecreate-react-native-moduleCLI tool, add the React Native for Windows package and then:
Include a folder calledwindowswith the Windows Runtime Component we have developed.
Optionally include any JavaScript wrapper we might have created to make easier consuming the module.
If you already have a module with iOS and Android support, you'll just need instead to follow step 1 and include the windows folder with the Windows Runtime Component in your existing project.
However, regardless of your scenario, the current React Native for Windows implementation lacks an important feature calledlinking. You'll remember that, in the previous section, we had to manually edit theApp.cppfile of the React Native project to load, through theReactPackageProviderclass, our Windows Runtime Component. On Android and iOS this operation isn't needed thanks to linking. You just need to run thereact-native linkcommand: it will take care of everything for you, without needing to manually edit the Android and iOS projects. React Native 0.60 has brought linking to the next level, by introducingautomatic linking. You don't even have to run thelinkcommand anymore; just add the package to your project and React Native will take of everything else for you. The React Native implementation for iOS and Android will automatically load all the 3rd party modules that have been added to the project. This feature isn't available on Windows yet and, as such, if you create an independent module and you publish it on NPM, you will still need to manually open the React Native for Windows solution in Visual Studio and register the module in theApp.cppfile.
Another important information to highlight is the language choice for your module. C++ has a performance advantage over C++, since it doesn't need to pull in the whole CLR in the React Native project. As such, especially if you're planning to share your module with the community, you should seriously consider to use C++. If, instead, you're aiming to use your module only in your project and you have verified that the performance trade off doesn't heavily affect your application, it's fine to keep using C#.
Publishing the application
Compared to the last time I wrote about React Native for Windows, there's a big news! Now you can create a deployable version of the application: an AppX / MSIX package which is self-contained and that doesn't need the Metro packager to be up & running. This way, you can publish the application on the Microsoft Store or using one of the many supported deployment techniques (sideloading, SSCM, Intune, etc.). To achieve this goal just start the publishing process, like you would do with any Universal Windows Platform application. Right click on the React Native project in Visual Studio, choosePublish → Create app packagesand follow the wizard. Just make sure, in the package configuration step, to chooseReleaseas configuration mode. This way, all the React Native resources will be bundled together, removing the requirement of having the Metro packager up & running.
Wrapping up
In this post we have seen how you can build native modules for React Native for Windows. This will allow us to build applications using the React stack but, at the same time, leverage native features provided by Windows, like toast notifications, access to Bluetooth APIs, file system, etc. We have also learned how we can distribute an application built with React Native, either via the Microsoft Store or one of the many techniques supported by MSIX, like sideloading or Microsoft Intune.
You can find the sample I've built for this poston GitHub.
Hi Matteo Pagani, thank you for the clear and detailed instruction! I have encounter one problem that I was not able to figure out: when adding references to my C# project, the "Shared Projects" section is empty and I was not able to get "Microsoft.ReactNative.SharedManaged" from anywhere. In a C++ project in the same solution the "Microsoft.ReactNative.Cxx" reference is available. My project is based on react-native-windows 70.0.3 and I found https://github.com/microsoft/react-native-windows/pull/5091 saying that SharedManaged was removed from the package, but I have no idea how to replace it or where was it moved. Any chance you could point me in the right direction, please?
Best regards,
Bogna
"}},"componentScriptGroups({\"componentId\":\"custom.widget.MicrosoftFooter\"})":{"__typename":"ComponentScriptGroups","scriptGroups":{"__typename":"ComponentScriptGroupsDefinition","afterInteractive":{"__typename":"PageScriptGroupDefinition","group":"AFTER_INTERACTIVE","scriptIds":[]},"lazyOnLoad":{"__typename":"PageScriptGroupDefinition","group":"LAZY_ON_LOAD","scriptIds":[]}},"componentScripts":[]},"cachedText({\"lastModified\":\"1745505310960\",\"locale\":\"en-US\",\"namespaces\":[\"components/community/NavbarDropdownToggle\"]})":[{"__ref":"CachedAsset:text:en_US-components/community/NavbarDropdownToggle-1745505310960"}],"cachedText({\"lastModified\":\"1745505310960\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/common/QueryHandler\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/common/QueryHandler-1745505310960"}],"cachedText({\"lastModified\":\"1745505310960\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageCoverImage\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageCoverImage-1745505310960"}],"cachedText({\"lastModified\":\"1745505310960\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/nodes/NodeTitle\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/nodes/NodeTitle-1745505310960"}],"cachedText({\"lastModified\":\"1745505310960\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageTimeToRead\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageTimeToRead-1745505310960"}],"cachedText({\"lastModified\":\"1745505310960\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageSubject\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageSubject-1745505310960"}],"cachedText({\"lastModified\":\"1745505310960\",\"locale\":\"en-US\",\"namespaces\":[\"components/users/UserLink\"]})":[{"__ref":"CachedAsset:text:en_US-components/users/UserLink-1745505310960"}],"cachedText({\"lastModified\":\"1745505310960\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/users/UserRank\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/users/UserRank-1745505310960"}],"cachedText({\"lastModified\":\"1745505310960\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageTime\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageTime-1745505310960"}],"cachedText({\"lastModified\":\"1745505310960\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageBody\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageBody-1745505310960"}],"cachedText({\"lastModified\":\"1745505310960\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageCustomFields\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageCustomFields-1745505310960"}],"cachedText({\"lastModified\":\"1745505310960\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageRevision\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageRevision-1745505310960"}],"cachedText({\"lastModified\":\"1745505310960\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageReplyButton\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageReplyButton-1745505310960"}],"cachedText({\"lastModified\":\"1745505310960\",\"locale\":\"en-US\",\"namespaces\":[\"components/messages/MessageAuthorBio\"]})":[{"__ref":"CachedAsset:text:en_US-components/messages/MessageAuthorBio-1745505310960"}],"cachedText({\"lastModified\":\"1745505310960\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/users/UserAvatar\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/users/UserAvatar-1745505310960"}],"cachedText({\"lastModified\":\"1745505310960\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/ranks/UserRankLabel\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/ranks/UserRankLabel-1745505310960"}],"cachedText({\"lastModified\":\"1745505310960\",\"locale\":\"en-US\",\"namespaces\":[\"components/users/UserRegistrationDate\"]})":[{"__ref":"CachedAsset:text:en_US-components/users/UserRegistrationDate-1745505310960"}],"cachedText({\"lastModified\":\"1745505310960\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/nodes/NodeAvatar\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/nodes/NodeAvatar-1745505310960"}],"cachedText({\"lastModified\":\"1745505310960\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/nodes/NodeDescription\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/nodes/NodeDescription-1745505310960"}],"message({\"id\":\"message:3659195\"})":{"__ref":"BlogReplyMessage:message:3659195"},"message({\"id\":\"message:3655756\"})":{"__ref":"BlogReplyMessage:message:3655756"},"cachedText({\"lastModified\":\"1745505310960\",\"locale\":\"en-US\",\"namespaces\":[\"shared/client/components/nodes/NodeIcon\"]})":[{"__ref":"CachedAsset:text:en_US-shared/client/components/nodes/NodeIcon-1745505310960"}]},"CachedAsset:pages-1745487429244":{"__typename":"CachedAsset","id":"pages-1745487429244","value":[{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"BlogViewAllPostsPage","type":"BLOG","urlPath":"/category/:categoryId/blog/:boardId/all-posts/(/:after|/:before)?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"CasePortalPage","type":"CASE_PORTAL","urlPath":"/caseportal","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"CreateGroupHubPage","type":"GROUP_HUB","urlPath":"/groups/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"CaseViewPage","type":"CASE_DETAILS","urlPath":"/case/:caseId/:caseNumber","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"InboxPage","type":"COMMUNITY","urlPath":"/inbox","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"HelpFAQPage","type":"COMMUNITY","urlPath":"/help","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"IdeaMessagePage","type":"IDEA_POST","urlPath":"/idea/:boardId/:messageSubject/:messageId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"IdeaViewAllIdeasPage","type":"IDEA","urlPath":"/category/:categoryId/ideas/:boardId/all-ideas/(/:after|/:before)?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"LoginPage","type":"USER","urlPath":"/signin","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"BlogPostPage","type":"BLOG","urlPath":"/category/:categoryId/blogs/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"UserBlogPermissions.Page","type":"COMMUNITY","urlPath":"/c/user-blog-permissions/page","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"ThemeEditorPage","type":"COMMUNITY","urlPath":"/designer/themes","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"TkbViewAllArticlesPage","type":"TKB","urlPath":"/category/:categoryId/kb/:boardId/all-articles/(/:after|/:before)?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1730142000000,"localOverride":null,"page":{"id":"AllEvents","type":"CUSTOM","urlPath":"/Events","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"OccasionEditPage","type":"EVENT","urlPath":"/event/:boardId/:messageSubject/:messageId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"OAuthAuthorizationAllowPage","type":"USER","urlPath":"/auth/authorize/allow","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"PageEditorPage","type":"COMMUNITY","urlPath":"/designer/pages","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"PostPage","type":"COMMUNITY","urlPath":"/category/:categoryId/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"ForumBoardPage","type":"FORUM","urlPath":"/category/:categoryId/discussions/:boardId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"TkbBoardPage","type":"TKB","urlPath":"/category/:categoryId/kb/:boardId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"EventPostPage","type":"EVENT","urlPath":"/category/:categoryId/events/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"UserBadgesPage","type":"COMMUNITY","urlPath":"/users/:login/:userId/badges","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"GroupHubMembershipAction","type":"GROUP_HUB","urlPath":"/membership/join/:nodeId/:membershipType","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"MaintenancePage","type":"COMMUNITY","urlPath":"/maintenance","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"IdeaReplyPage","type":"IDEA_REPLY","urlPath":"/idea/:boardId/:messageSubject/:messageId/comments/:replyId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"UserSettingsPage","type":"USER","urlPath":"/mysettings/:userSettingsTab","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"GroupHubsPage","type":"GROUP_HUB","urlPath":"/groups","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"ForumPostPage","type":"FORUM","urlPath":"/category/:categoryId/discussions/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"OccasionRsvpActionPage","type":"OCCASION","urlPath":"/event/:boardId/:messageSubject/:messageId/rsvp/:responseType","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"VerifyUserEmailPage","type":"USER","urlPath":"/verifyemail/:userId/:verifyEmailToken","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"AllOccasionsPage","type":"OCCASION","urlPath":"/category/:categoryId/events/:boardId/all-events/(/:after|/:before)?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"EventBoardPage","type":"EVENT","urlPath":"/category/:categoryId/events/:boardId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"TkbReplyPage","type":"TKB_REPLY","urlPath":"/kb/:boardId/:messageSubject/:messageId/comments/:replyId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"IdeaBoardPage","type":"IDEA","urlPath":"/category/:categoryId/ideas/:boardId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"CommunityGuideLinesPage","type":"COMMUNITY","urlPath":"/communityguidelines","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"CaseCreatePage","type":"SALESFORCE_CASE_CREATION","urlPath":"/caseportal/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"TkbEditPage","type":"TKB","urlPath":"/kb/:boardId/:messageSubject/:messageId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"ForgotPasswordPage","type":"USER","urlPath":"/forgotpassword","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"IdeaEditPage","type":"IDEA","urlPath":"/idea/:boardId/:messageSubject/:messageId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"TagPage","type":"COMMUNITY","urlPath":"/tag/:tagName","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"BlogBoardPage","type":"BLOG","urlPath":"/category/:categoryId/blog/:boardId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"OccasionMessagePage","type":"OCCASION_TOPIC","urlPath":"/event/:boardId/:messageSubject/:messageId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"ManageContentPage","type":"COMMUNITY","urlPath":"/managecontent","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"ClosedMembershipNodeNonMembersPage","type":"GROUP_HUB","urlPath":"/closedgroup/:groupHubId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"CommunityPage","type":"COMMUNITY","urlPath":"/","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"ForumMessagePage","type":"FORUM_TOPIC","urlPath":"/discussions/:boardId/:messageSubject/:messageId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"IdeaPostPage","type":"IDEA","urlPath":"/category/:categoryId/ideas/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1730142000000,"localOverride":null,"page":{"id":"CommunityHub.Page","type":"CUSTOM","urlPath":"/Directory","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"BlogMessagePage","type":"BLOG_ARTICLE","urlPath":"/blog/:boardId/:messageSubject/:messageId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"RegistrationPage","type":"USER","urlPath":"/register","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"EditGroupHubPage","type":"GROUP_HUB","urlPath":"/group/:groupHubId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"ForumEditPage","type":"FORUM","urlPath":"/discussions/:boardId/:messageSubject/:messageId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"ResetPasswordPage","type":"USER","urlPath":"/resetpassword/:userId/:resetPasswordToken","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1730142000000,"localOverride":null,"page":{"id":"AllBlogs.Page","type":"CUSTOM","urlPath":"/blogs","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"TkbMessagePage","type":"TKB_ARTICLE","urlPath":"/kb/:boardId/:messageSubject/:messageId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"BlogEditPage","type":"BLOG","urlPath":"/blog/:boardId/:messageSubject/:messageId/edit","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"ManageUsersPage","type":"USER","urlPath":"/users/manage/:tab?/:manageUsersTab?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"ForumReplyPage","type":"FORUM_REPLY","urlPath":"/discussions/:boardId/:messageSubject/:messageId/replies/:replyId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"PrivacyPolicyPage","type":"COMMUNITY","urlPath":"/privacypolicy","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"NotificationPage","type":"COMMUNITY","urlPath":"/notifications","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"UserPage","type":"USER","urlPath":"/users/:login/:userId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"OccasionReplyPage","type":"OCCASION_REPLY","urlPath":"/event/:boardId/:messageSubject/:messageId/comments/:replyId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"ManageMembersPage","type":"GROUP_HUB","urlPath":"/group/:groupHubId/manage/:tab?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"SearchResultsPage","type":"COMMUNITY","urlPath":"/search","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"BlogReplyPage","type":"BLOG_REPLY","urlPath":"/blog/:boardId/:messageSubject/:messageId/replies/:replyId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"GroupHubPage","type":"GROUP_HUB","urlPath":"/group/:groupHubId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"TermsOfServicePage","type":"COMMUNITY","urlPath":"/termsofservice","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"CategoryPage","type":"CATEGORY","urlPath":"/category/:categoryId","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"ForumViewAllTopicsPage","type":"FORUM","urlPath":"/category/:categoryId/discussions/:boardId/all-topics/(/:after|/:before)?","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"TkbPostPage","type":"TKB","urlPath":"/category/:categoryId/kbs/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"},{"lastUpdatedTime":1745487429244,"localOverride":null,"page":{"id":"GroupHubPostPage","type":"GROUP_HUB","urlPath":"/group/:groupHubId/:boardId/create","__typename":"PageDescriptor"},"__typename":"PageResource"}],"localOverride":false},"CachedAsset:text:en_US-components/context/AppContext/AppContextProvider-0":{"__typename":"CachedAsset","id":"text:en_US-components/context/AppContext/AppContextProvider-0","value":{"noCommunity":"Cannot find community","noUser":"Cannot find current user","noNode":"Cannot find node with id {nodeId}","noMessage":"Cannot find message with id {messageId}"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/common/Loading/LoadingDot-0":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/common/Loading/LoadingDot-0","value":{"title":"Loading..."},"localOverride":false},"User:user:-1":{"__typename":"User","id":"user:-1","uid":-1,"login":"Deleted","email":"","avatar":null,"rank":null,"kudosWeight":1,"registrationData":{"__typename":"RegistrationData","status":"ANONYMOUS","registrationTime":null,"confirmEmailStatus":false,"registrationAccessLevel":"VIEW","ssoRegistrationFields":[]},"ssoId":null,"profileSettings":{"__typename":"ProfileSettings","dateDisplayStyle":{"__typename":"InheritableStringSettingWithPossibleValues","key":"layout.friendly_dates_enabled","value":"false","localValue":"true","possibleValues":["true","false"]},"dateDisplayFormat":{"__typename":"InheritableStringSetting","key":"layout.format_pattern_date","value":"MMM dd yyyy","localValue":"MM-dd-yyyy"},"language":{"__typename":"InheritableStringSettingWithPossibleValues","key":"profile.language","value":"en-US","localValue":"en","possibleValues":["en-US"]}},"deleted":false},"Theme:customTheme1":{"__typename":"Theme","id":"customTheme1"},"Category:category:microsoft365":{"__typename":"Category","id":"category:microsoft365","entityType":"CATEGORY","displayId":"microsoft365","nodeType":"category","depth":3,"title":"Microsoft 365","shortTitle":"Microsoft 365","parent":{"__ref":"Category:category:products-services"},"categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:top":{"__typename":"Category","id":"category:top","displayId":"top","nodeType":"category","depth":0,"title":"Top","entityType":"CATEGORY","shortTitle":"Top"},"Category:category:communities":{"__typename":"Category","id":"category:communities","displayId":"communities","nodeType":"category","depth":1,"parent":{"__ref":"Category:category:top"},"title":"Communities","entityType":"CATEGORY","shortTitle":"Communities"},"Category:category:products-services":{"__typename":"Category","id":"category:products-services","displayId":"products-services","nodeType":"category","depth":2,"parent":{"__ref":"Category:category:communities"},"title":"Products","entityType":"CATEGORY","shortTitle":"Products"},"Blog:board:ModernWorkAppConsult":{"__typename":"Blog","id":"board:ModernWorkAppConsult","entityType":"BLOG","displayId":"ModernWorkAppConsult","nodeType":"board","depth":4,"conversationStyle":"BLOG","title":"Modern Work App Consult Blog","description":"","avatar":null,"profileSettings":{"__typename":"ProfileSettings","language":null},"parent":{"__ref":"Category:category:microsoft365"},"ancestors":{"__typename":"CoreNodeConnection","edges":[{"__typename":"CoreNodeEdge","node":{"__ref":"Community:community:gxcuf89792"}},{"__typename":"CoreNodeEdge","node":{"__ref":"Category:category:communities"}},{"__typename":"CoreNodeEdge","node":{"__ref":"Category:category:products-services"}},{"__typename":"CoreNodeEdge","node":{"__ref":"Category:category:microsoft365"}}]},"userContext":{"__typename":"NodeUserContext","canAddAttachments":false,"canUpdateNode":false,"canPostMessages":false,"isSubscribed":false},"boardPolicies":{"__typename":"BoardPolicies","canPublishArticleOnCreate":{"__typename":"PolicyResult","failureReason":{"__typename":"FailureReason","message":"error.lithium.policies.forums.policy_can_publish_on_create_workflow_action.accessDenied","key":"error.lithium.policies.forums.policy_can_publish_on_create_workflow_action.accessDenied","args":[]}}},"shortTitle":"Modern Work App Consult Blog","repliesProperties":{"__typename":"RepliesProperties","sortOrder":"REVERSE_PUBLISH_TIME","repliesFormat":"threaded"},"eventPath":"category:microsoft365/category:products-services/category:communities/community:gxcuf89792board:ModernWorkAppConsult/","tagProperties":{"__typename":"TagNodeProperties","tagsEnabled":{"__typename":"PolicyResult","failureReason":null}},"requireTags":true,"tagType":"PRESET_ONLY"},"AssociatedImage:{\"url\":\"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/cmstNC05WEo0blc\"}":{"__typename":"AssociatedImage","url":"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/cmstNC05WEo0blc","height":512,"width":512,"mimeType":"image/png"},"Rank:rank:4":{"__typename":"Rank","id":"rank:4","position":6,"name":"Microsoft","color":"333333","icon":{"__ref":"AssociatedImage:{\"url\":\"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/cmstNC05WEo0blc\"}"},"rankStyle":"OUTLINE"},"User:user:44595":{"__typename":"User","id":"user:44595","uid":44595,"login":"Matteo Pagani","deleted":false,"avatar":{"__typename":"UserAvatar","url":"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/dS00NDU5NS02MzA4OGkwNTU0MkU2NzQwODM2NTE0"},"rank":{"__ref":"Rank:rank:4"},"email":"","messagesCount":117,"biography":null,"topicsCount":39,"kudosReceivedCount":132,"kudosGivenCount":1,"kudosWeight":1,"registrationData":{"__typename":"RegistrationData","status":null,"registrationTime":"2017-03-19T04:38:14.936-07:00","confirmEmailStatus":null},"followersCount":null,"solutionsCount":1,"entityType":"USER","eventPath":"community:gxcuf89792/user:44595"},"BlogTopicMessage:message:1067893":{"__typename":"BlogTopicMessage","uid":1067893,"subject":"Building a React Native module for Windows","id":"message:1067893","revisionNum":4,"repliesCount":2,"author":{"__ref":"User:user:44595"},"depth":0,"hasGivenKudo":false,"board":{"__ref":"Blog:board:ModernWorkAppConsult"},"conversation":{"__ref":"Conversation:conversation:1067893"},"messagePolicies":{"__typename":"MessagePolicies","canPublishArticleOnEdit":{"__typename":"PolicyResult","failureReason":{"__typename":"FailureReason","message":"error.lithium.policies.forums.policy_can_publish_on_edit_workflow_action.accessDenied","key":"error.lithium.policies.forums.policy_can_publish_on_edit_workflow_action.accessDenied","args":[]}},"canModerateSpamMessage":{"__typename":"PolicyResult","failureReason":{"__typename":"FailureReason","message":"error.lithium.policies.feature.moderation_spam.action.moderate_entity.allowed.accessDenied","key":"error.lithium.policies.feature.moderation_spam.action.moderate_entity.allowed.accessDenied","args":[]}}},"contentWorkflow":{"__typename":"ContentWorkflow","state":"PUBLISH","scheduledPublishTime":null,"scheduledTimezone":null,"userContext":{"__typename":"MessageWorkflowContext","canSubmitForReview":null,"canEdit":false,"canRecall":null,"canSubmitForPublication":null,"canReturnToAuthor":null,"canPublish":null,"canReturnToReview":null,"canSchedule":false},"shortScheduledTimezone":null},"readOnly":false,"editFrozen":false,"moderationData":{"__ref":"ModerationData:moderation_data:1067893"},"teaser":"
In this blog post we're going to learn how we can build such a native module with Windows support, so that we can access to native Windows APIs when our React Native application is running on Windows.
","body":"
Update on 2nd January 2020:
\n
The latest version of React Native for Windows has changed the namespace in which the IReactPackageProvider interface has been implemented. The previous one was:
\n
\n
\n
\n
Microsoft.ReactNative.Bridge;
\n
\n
\n
\n
while the new one is:
\n
\n
\n
\n
Microsoft.ReactNative;
\n
\n
\n
\n
The snippets in the post have been updated to reflect this change.
\n
\n
\n
React Native offers a wide range of modules to extend the core functionalities of the platform, developed either by the community or by the React team himself. These modules range from UI controls, helpers and services. Some of them are \"generic\" and they perform tasks which don't require access to any specific platform feature. For example,Reduxis a very popular state management library used in React applications but since, by default, everything is stored in memory, it basically runs everywhere. In fact, the same library can be used either with web applications (using React) or native applications (using React Native).
\n
\n
However, there are many scenarios in the native development space where the only way to achieve a task is by interacting with the native APIs offered by the platform. Saving a file in the local storage; getting information about the device; accessing to the GPS are just a few examples. In this case, the module can't be generic, because each platform has its own way to access to native features. This is why React Native supports the concept ofnative modules: a module that is leveraged in the main application through JavaScript but that, under the hood, invokes a native component specific for the platform where the application is running. Being native, each component isn't built in JavaScript, but using the native language used by the platform. For example, take a look at the popular moduleAsyncStorage, which can be used to read and write files in the local storage. As you can see, we have a folder calledios, which contains a XCode project; theandroidfolder, instead, contains some Java files. Both projects simply expose the same features, but implemented using the specific language and APIs from iOS and Android.
\n
\n
If you have some experience with Xamarin and Xamarin Forms, this approach will be familiar. It's the same exact concept, in fact, behind Xamarin Plugins: the entry point is a C# wrapper which, under the hood, calls Swift, Java or UWP code based on the platform where the application is running.
\n
\n
In this blog post we're going to learn how we can build such a native module with Windows support, so that we can access to native Windows APIs when our React Native application is running on Windows.
\n
\n
Create a React Native for Windows project
\n
As first step, let's create a React Native project with Windows support. I won't explain all the steps, since they are very well documentedon the official GitHub repository. Additionally, I already introduced React Native and the recently added Windows supportin another article.
\n
Once you have completed all the steps, open thewindowsfolder inside the project and double click on the Visual Studio solution. As we have learnedin the previous article, the solution contains a single application which is the host for our React Native content. In a regular scenario you won't need to touch the code here, since the whole development will happen in the JavaScript world. However, in our case we have to make an exception, since we need to interact with native Windows APIs to build our module.
\n
\n
Create the module with a Windows Runtime Component
\n
Let's start by adding a new project to the solution, which type is Windows Runtime Component. The component will give us access to all the Universal Windows Platform APIs. Additionally, being a WinRT component, it can be consumed regardless of the language used to build the main application. As a consequence, you're free to create a C# or C++ component, based on the language you're most familiar with. We'll be able to leverage it from the main React Native host (which is built in C++) regardless.
\n
In my case, C# is my favorite language, so I've chosen theWindows Runtime Component (C#)template. If you want to use C++, instead, you need to choose theWindows Runtime Component (C++/WinRT)template. If you don't see it, you can install it usingthis extension. Pay attention to not use the C++/CX template, because this approach to build C++ components for the Universal Windows Platform has been deprecated.
\n
\n
\n
\n
Once the project has been created, you need to reference a couple of projects which are already included as part of the React Native for Windows solution. Right click on the project you have just created and chooseAdd → Reference.
\n
\n
From theProjectssection, chooseMicrosoft.ReactNative. This is the core React Native implementation for Windows.
\n
From theShared Projectssection, chooseMicrosoft.ReactNative.SharedManaged. This project will give us access to C# helpers that will make easy to build a native module. If you're using C++, the project to reference is calledMicrosoft.ReactNative.Cxx.
\n
\n
These projects are very important because they define many attributes which will allow you to export the native code to JavaScript. This way, the developers who are going to use your module in their React Native projects don't need to know anything about C++, C# or Windows 10 APIs. They just need to use standard and plain JavaScript code.
\n
\n
Before starting to write some code, expand theReferencessection of the project, select theMicrosoft.ReactNativelibrary, right click on it and chooseProperties. SetCopy Localtofalse, to make sure that there are no conflicts with the main application.
\n
Now we can start building our module. We're going to create a simple one to expose the information about the model of the device where the application is running. Let's start by renaming the default class included in the project with a more meaningful name, likeSampleComponent. This should trigger the renaming also of the class declared inside the code.
\n
Now we just need to define one or more methods, that will be exposed to the React Native application. In our case, we're going to define a single method that will return the current device name, using theEasClientDeviceInformationclass included in the Universal Windows Platform:
\n
\n
using Windows.Security.ExchangeActiveSyncProvisioning;\n\nnamespaceSampleReactModule\n{\n classSampleComponent\n {\n publicstringGetDeviceModel()\n {\n EasClientDeviceInformation info = new EasClientDeviceInformation();\n return info.SystemProductName;\n }\n }\n}\n
\n
The next step is to leverage the attributes provided by theMicrosoft.ReactNativelibrary to export the class and the method we have created to React Native. In our scenario we're going to use two of the many available ones:
\n
\n
[ReactModule], which we're using to decorate the whole class.
\n
[ReactMethod], which we're using to decorate every single method we want to expose.
\n
\n
This is the final look of our class:
\n
\n
using Microsoft.ReactNative.Managed;\nusing Windows.Security.ExchangeActiveSyncProvisioning;\n\nnamespaceSampleReactModule\n{\n [ReactModule]\n classSampleComponent\n {\n [ReactMethod(\"getDeviceModel\")]\n publicstringGetDeviceModel()\n {\n EasClientDeviceInformation info = new EasClientDeviceInformation();\n return info.SystemProductName;\n }\n }\n}\n
\n
Notice that we have passed thegetDeviceModelvalue as parameter of theReactMethodattribute. This is how we define the name of the method that will be available through JavaScript to the React Native project.
\n
There are many other attributes, which can be used to expose additional stuff to the React Native application, like properties or event handlers. They are all documentedhere.
\n
The next thing we need is aReactPackageProvider, which is a special object that will allow the React Native project to load all the modules exposed by our library. By default, in fact, a React Native application loads only the built-in modules. Right click on the project and chooseAdd → Class. Name itReactPackageProvider. The class is very simple:
It just needs to implement theIReactPackageProviderinterface, which will require you to implement theCreatePackage()method. Inside it we leverage thepackageBuilderobject and theAddAttributedModules()method to load all the modules we have decorated with the[ReactModule]attribute inside our project.
\n
\n
Register the module in the React Native application
\n
As already mentioned, by default React Native for Windows registers only the modules which are included in the main project. If we would have created theSampleComponentclass inside the main React Native project, we wouldn't have needed any additional step. However, in our case we are using a separate Windows Runtime Component, which allows us to leverage a different language (C#), so we need an extra step to register the library in the main application.
\n
\n
First, right click on the React Native for Windows project and chooseAdd → Reference. Select from the list the name of the Windows Runtime Component you have just created (in my sample, it'sSampleReactModule) and press Ok.
\n
\n
Expand theApp.xamlnode in the main React Native project and double click on theApp.cppfile. Inside theAppconstructor you will find the following entry:
SampleReactModuleis the name of the Windows Runtime Component we have previously created, thus it's the namespace that contains ourReactPackageProviderimplementation. To make this code compiling we need also to include the header file of our module at the top:
\n
\n
#include\"winrt/SampleReactModule.h\"\n
\n
This is how the wholeApp.cppfile should look like:
\n
\n
#include\"pch.h\"\n\n#include\"App.h\"\n#include\"ReactPackageProvider.h\"\n#include\"winrt/SampleReactModule.h\"\n\nusingnamespace winrt::NativeModuleSample;\nusingnamespace winrt::NativeModuleSample::implementation;\n\n/// <summary>\n/// Initializes the singleton application object. This is the first line of\n/// authored code executed, and as such is the logical equivalent of main() or\n/// WinMain().\n/// </summary>\nApp::App() noexcept\n{\n MainComponentName(L\"NativeModuleSample\");\n\n#if BUNDLE\n JavaScriptBundleFile(L\"index.windows\");\n InstanceSettings().UseWebDebugger(false);\n InstanceSettings().UseLiveReload(false);\n#else\n JavaScriptMainModuleName(L\"index\");\n InstanceSettings().UseWebDebugger(true);\n InstanceSettings().UseLiveReload(true);\n#endif\n\n#if _DEBUG\n InstanceSettings().EnableDeveloperMenu(true);\n#else\n InstanceSettings().EnableDeveloperMenu(false);\n#endif\n\n PackageProviders().Append(make<ReactPackageProvider>()); // Includes all modules in this project\n PackageProviders().Append(winrt::SampleReactModule::ReactPackageProvider());\n\n\n InitializeComponent();\n\n // This works around a cpp/winrt bug with composable/aggregable types tracked\n // by 22116519\n AddRef();\n m_inner.as<::IUnknown>()->Release();\n}
\n
\n
Using the module from JavaScript
\n
That's it! Now we can move to the React Native project to start using the module we have just built from JavaScript. Open the folder which contains your React Native project with your favorite web editor. For me, it's Visual Studio Code. For simplicity, I've implemented the component which uses the native module directly in theApp.jsfile of my React Native project, so that it will be the startup page.
\n
The first step is importing theNativeModuleobject exposed by React Native in our component:
\n
\n
import { NativeModules } from'react-native';\n
\n
Our module will be exposed through this object, by leveraging the name of the class we have decorated with the[ReactModule]attribute. In our case, it'sSampleComponent, so we can use the following code to access to theGetDeviceModule()method:
\n
\n
NativeModules.SampleComponent.getDeviceModel();\n
\n
Notice that we can reference the method with the lowercase name,getDeviceModel(), thanks to the parameter we have passed to the[ReactMethod]attribute. However, by default the methods exposed by native modules are implemented in an asynchronous way using callbacks. As such, if we want to consume thegetDeviceModel()method, we need to use the following approach:
\n
\n
getModel = () => {\n var current = this;\n NativeModules.SampleComponent.getDeviceModel(function(result) {\n current.setState({model: result});\n })\n}\n
\n
The method accepts a function, that is invoked once the asynchronous operation is completed. Inside this function we receive, as parameter, the result returned by our native method (in our case, the model of the device). In our sample, we store it inside the component's state, so that we can display it in the user interface using the JSX syntax:
\n
\n
<Text>Model: {this.state.model}</Text>\n
\n
Before calling thesetState()method, however, we need to save a reference to the main context. Inside the callback, in fact, we have moved to a different context, so we don't have access to the helpers exposed by React Native.
\n
Below you can find the full definition of our component:
If you're familiar with React Native, the code should be easy to understand:
\n\n
We have defined a function calledgetModel(), which takes care of interacting with our native module. The model of the device is stored inside the component's state, using thesetState()function provided by React Native.
\n
Therender()methods defines the UI component, using the JSX syntax. The UI is very simple:\n
\n
We have a<Button>, which invokes thegetModel()function by leveraging theonPressevent.
\n
We have a<Text>, which displays the value of themodelproperty stored inside the state.
\n
\n
\n\n
\n
Testing the code
\n
Now it's time to test the code! First, right click on the React Native project in Visual Studio and chooseDeploy. Before doing it, however, use theConfiguration Managerdropdown to make sure you're targeting the right CPU architecture. By default, in fact, the solution will target ARM, so you will have to switch to x86 or x64. If it's the first time you build it, it will take a while since C++ compilation isn't exactly fast 😃 Once it's done, open a command prompt on the folder which contains your React Native project and typeyarn start. This will launch the Metro packager, which will serve all the React Native components to your application. Once the dependency graph has been loaded, you can open the Start menu and launch the Windows application you have just deployed from Visual Studio. If you have done everything in the correct way, you will see the very simple UI we have built in our component. By pressing the button you will invoke the native module and you will see the model of your device being displayed:
\n
\n
\n
Improving the code
\n
If you compare this development experience with the one you have when you add a 3rd party module to your project using yarn or npm, you realize that it's quite different. In such a case, in fact, you don't have to deal with theNativeModulesobject; or you don't need to use callbacks to call the various methods. Let's use again the example of the popularAsyncStoragemodule. If you want to store some data in the storage, you just import a reference to theAsyncStorageobject and then you call an asynchronous method calledsetItem:
Can we achieve the same goal with the native module we have just built? The answer is yes! We just need to build a JavaScript wrapper, that will make consuming our native module more straightforward.
\n
In Visual Studio Code add a new file in your React Native project and call itSampleComponent.js. First, let's import the sameNativeModulesobject we have previously imported in the main component:
\n
\n
import { NativeModules } from'react-native';\n
\n
Now, usingJavaScript Promises, we can build a wrapper to thegetResult()method. Thanks to Promises, we can enable an easier approach to consume our asynchronous API, thanks to theasyncandawaitkeywords. If you have some C# background, this approach is similar to use theTaskCompletionSourceclass to build asynchronous operations that returns aTaskobject-
The method returns a newPromise, which requires us to fulfill two objects:
\n
\n
resolve, which is invoked when the operation has completed successfully.
\n
reject, which is invoked when the operation has failed.
\n
\n
Inside the Promise we invoke thegetDeviceModel()method, in the same way we were doing before in the component. The only difference is that, in this case, once the callback is completed we pass theresultto theresolve()method in case of success; otherwise, we pass theerrorto thereject()method.
\n
Now that we have built our wrapper, we can simplify the component we have previously built. First, remove the following line:
\n
\n
import { NativeModules } from'react-native';\n
\n
Then replace it with the following one:
\n
\n
import * as SampleComponent from'./SampleComponent'\n
\n
This import will expose the functions we have defined in our wrapper through theSampleComponentobject. Now you can change thegetModel()function simply with this code:
\n
\n
getModel = async () => {\n var model = await SampleComponent.getDeviceModel();\n this.setState( { model: model});\n}\n
\n
As you can see, thanks to Promises and theasyncandawaitkeywords, the code is simpler to write and read. We just need to mark the function asasync, then call theSampleComponent.getDeviceModel()function with theawaitprefix. Since the asynchronous operation is managed for us, we can treat it like if it's synchronous and just store in a variable the result, which will contain the device model. Since we aren't using callbacks, we can also set the state directly by callingthis.setState(). If you are a C# developer, everything should be very familiar, since it's the same async and await approach supported by C#.
\n
That's it! Now launch the application again. In this case we didn't touch the native project, so if the Metro packager was still running, we should already be seeing the updated version. Of course there won't be any difference, since we didn't change the UI or the behavior, but the code is easier to read and maintain now.
\n
\n
Exporting the module
\n
So far, we have added the native module implementation directly in the project. What if we want to create a true self-contained module, that we can install using yarn or npm like any other 3rd party module? Well, unfortunately the story isn't complete for Windows yet. By following the documentationavailable on GitHubit's easy to create the skeleton of the module. We just need to create a new React Native module project, using thecreate-react-native-moduleCLI tool, add the React Native for Windows package and then:
\n
\n\n
Include a folder calledwindowswith the Windows Runtime Component we have developed.
\n
Optionally include any JavaScript wrapper we might have created to make easier consuming the module.
\n\n
If you already have a module with iOS and Android support, you'll just need instead to follow step 1 and include the windows folder with the Windows Runtime Component in your existing project.
\n
However, regardless of your scenario, the current React Native for Windows implementation lacks an important feature calledlinking. You'll remember that, in the previous section, we had to manually edit theApp.cppfile of the React Native project to load, through theReactPackageProviderclass, our Windows Runtime Component. On Android and iOS this operation isn't needed thanks to linking. You just need to run thereact-native linkcommand: it will take care of everything for you, without needing to manually edit the Android and iOS projects. React Native 0.60 has brought linking to the next level, by introducingautomatic linking. You don't even have to run thelinkcommand anymore; just add the package to your project and React Native will take of everything else for you. The React Native implementation for iOS and Android will automatically load all the 3rd party modules that have been added to the project. This feature isn't available on Windows yet and, as such, if you create an independent module and you publish it on NPM, you will still need to manually open the React Native for Windows solution in Visual Studio and register the module in theApp.cppfile.
\n
\n
Another important information to highlight is the language choice for your module. C++ has a performance advantage over C++, since it doesn't need to pull in the whole CLR in the React Native project. As such, especially if you're planning to share your module with the community, you should seriously consider to use C++. If, instead, you're aiming to use your module only in your project and you have verified that the performance trade off doesn't heavily affect your application, it's fine to keep using C#.
\n
Publishing the application
\n
Compared to the last time I wrote about React Native for Windows, there's a big news! Now you can create a deployable version of the application: an AppX / MSIX package which is self-contained and that doesn't need the Metro packager to be up & running. This way, you can publish the application on the Microsoft Store or using one of the many supported deployment techniques (sideloading, SSCM, Intune, etc.). To achieve this goal just start the publishing process, like you would do with any Universal Windows Platform application. Right click on the React Native project in Visual Studio, choosePublish → Create app packagesand follow the wizard. Just make sure, in the package configuration step, to chooseReleaseas configuration mode. This way, all the React Native resources will be bundled together, removing the requirement of having the Metro packager up & running.
\n
\n
Wrapping up
\n
In this post we have seen how you can build native modules for React Native for Windows. This will allow us to build applications using the React stack but, at the same time, leverage native features provided by Windows, like toast notifications, access to Bluetooth APIs, file system, etc. We have also learned how we can distribute an application built with React Native, either via the Microsoft Store or one of the many techniques supported by MSIX, like sideloading or Microsoft Intune.
\n
\n
You can find the sample I've built for this poston GitHub.
\n
\n
Happy coding!
","body@stringLength":"39874","rawBody":"
Update on 2nd January 2020:
\n
The latest version of React Native for Windows has changed the namespace in which the IReactPackageProvider interface has been implemented. The previous one was:
\n
\n
\n
\nMicrosoft.ReactNative.Bridge;\n
\n
\n
\n
while the new one is:
\n
\n
\n
\nMicrosoft.ReactNative;\n
\n
\n
\n
The snippets in the post have been updated to reflect this change.
\n
\n
\n
React Native offers a wide range of modules to extend the core functionalities of the platform, developed either by the community or by the React team himself. These modules range from UI controls, helpers and services. Some of them are \"generic\" and they perform tasks which don't require access to any specific platform feature. For example,Reduxis a very popular state management library used in React applications but since, by default, everything is stored in memory, it basically runs everywhere. In fact, the same library can be used either with web applications (using React) or native applications (using React Native).
\n
\n
However, there are many scenarios in the native development space where the only way to achieve a task is by interacting with the native APIs offered by the platform. Saving a file in the local storage; getting information about the device; accessing to the GPS are just a few examples. In this case, the module can't be generic, because each platform has its own way to access to native features. This is why React Native supports the concept ofnative modules: a module that is leveraged in the main application through JavaScript but that, under the hood, invokes a native component specific for the platform where the application is running. Being native, each component isn't built in JavaScript, but using the native language used by the platform. For example, take a look at the popular moduleAsyncStorage, which can be used to read and write files in the local storage. As you can see, we have a folder calledios, which contains a XCode project; theandroidfolder, instead, contains some Java files. Both projects simply expose the same features, but implemented using the specific language and APIs from iOS and Android.
\n
\n
If you have some experience with Xamarin and Xamarin Forms, this approach will be familiar. It's the same exact concept, in fact, behind Xamarin Plugins: the entry point is a C# wrapper which, under the hood, calls Swift, Java or UWP code based on the platform where the application is running.
\n
\n
In this blog post we're going to learn how we can build such a native module with Windows support, so that we can access to native Windows APIs when our React Native application is running on Windows.
\n
\n
Create a React Native for Windows project
\n
As first step, let's create a React Native project with Windows support. I won't explain all the steps, since they are very well documentedon the official GitHub repository. Additionally, I already introduced React Native and the recently added Windows supportin another article.
\n
Once you have completed all the steps, open thewindowsfolder inside the project and double click on the Visual Studio solution. As we have learnedin the previous article, the solution contains a single application which is the host for our React Native content. In a regular scenario you won't need to touch the code here, since the whole development will happen in the JavaScript world. However, in our case we have to make an exception, since we need to interact with native Windows APIs to build our module.
\n
\n
Create the module with a Windows Runtime Component
\n
Let's start by adding a new project to the solution, which type is Windows Runtime Component. The component will give us access to all the Universal Windows Platform APIs. Additionally, being a WinRT component, it can be consumed regardless of the language used to build the main application. As a consequence, you're free to create a C# or C++ component, based on the language you're most familiar with. We'll be able to leverage it from the main React Native host (which is built in C++) regardless.
\n
In my case, C# is my favorite language, so I've chosen theWindows Runtime Component (C#)template. If you want to use C++, instead, you need to choose theWindows Runtime Component (C++/WinRT)template. If you don't see it, you can install it usingthis extension. Pay attention to not use the C++/CX template, because this approach to build C++ components for the Universal Windows Platform has been deprecated.
\n
\n
\n
\n
Once the project has been created, you need to reference a couple of projects which are already included as part of the React Native for Windows solution. Right click on the project you have just created and chooseAdd → Reference.
\n
\n
From theProjectssection, chooseMicrosoft.ReactNative. This is the core React Native implementation for Windows.
\n
From theShared Projectssection, chooseMicrosoft.ReactNative.SharedManaged. This project will give us access to C# helpers that will make easy to build a native module. If you're using C++, the project to reference is calledMicrosoft.ReactNative.Cxx.
\n
\n
These projects are very important because they define many attributes which will allow you to export the native code to JavaScript. This way, the developers who are going to use your module in their React Native projects don't need to know anything about C++, C# or Windows 10 APIs. They just need to use standard and plain JavaScript code.
\n
\n
Before starting to write some code, expand theReferencessection of the project, select theMicrosoft.ReactNativelibrary, right click on it and chooseProperties. SetCopy Localtofalse, to make sure that there are no conflicts with the main application.
\n
Now we can start building our module. We're going to create a simple one to expose the information about the model of the device where the application is running. Let's start by renaming the default class included in the project with a more meaningful name, likeSampleComponent. This should trigger the renaming also of the class declared inside the code.
\n
Now we just need to define one or more methods, that will be exposed to the React Native application. In our case, we're going to define a single method that will return the current device name, using theEasClientDeviceInformationclass included in the Universal Windows Platform:
\n
\n
using Windows.Security.ExchangeActiveSyncProvisioning;\n\nnamespaceSampleReactModule\n{\n classSampleComponent\n {\n publicstringGetDeviceModel()\n {\n EasClientDeviceInformation info = new EasClientDeviceInformation();\n return info.SystemProductName;\n }\n }\n}\n
\n
The next step is to leverage the attributes provided by theMicrosoft.ReactNativelibrary to export the class and the method we have created to React Native. In our scenario we're going to use two of the many available ones:
\n
\n
[ReactModule], which we're using to decorate the whole class.
\n
[ReactMethod], which we're using to decorate every single method we want to expose.
\n
\n
This is the final look of our class:
\n
\n
using Microsoft.ReactNative.Managed;\nusing Windows.Security.ExchangeActiveSyncProvisioning;\n\nnamespaceSampleReactModule\n{\n [ReactModule]\n classSampleComponent\n {\n [ReactMethod(\"getDeviceModel\")]\n publicstringGetDeviceModel()\n {\n EasClientDeviceInformation info = new EasClientDeviceInformation();\n return info.SystemProductName;\n }\n }\n}\n
\n
Notice that we have passed thegetDeviceModelvalue as parameter of theReactMethodattribute. This is how we define the name of the method that will be available through JavaScript to the React Native project.
\n
There are many other attributes, which can be used to expose additional stuff to the React Native application, like properties or event handlers. They are all documentedhere.
\n
The next thing we need is aReactPackageProvider, which is a special object that will allow the React Native project to load all the modules exposed by our library. By default, in fact, a React Native application loads only the built-in modules. Right click on the project and chooseAdd → Class. Name itReactPackageProvider. The class is very simple:
It just needs to implement theIReactPackageProviderinterface, which will require you to implement theCreatePackage()method. Inside it we leverage thepackageBuilderobject and theAddAttributedModules()method to load all the modules we have decorated with the[ReactModule]attribute inside our project.
\n
\n
Register the module in the React Native application
\n
As already mentioned, by default React Native for Windows registers only the modules which are included in the main project. If we would have created theSampleComponentclass inside the main React Native project, we wouldn't have needed any additional step. However, in our case we are using a separate Windows Runtime Component, which allows us to leverage a different language (C#), so we need an extra step to register the library in the main application.
\n
\n
First, right click on the React Native for Windows project and chooseAdd → Reference. Select from the list the name of the Windows Runtime Component you have just created (in my sample, it'sSampleReactModule) and press Ok.
\n
\n
Expand theApp.xamlnode in the main React Native project and double click on theApp.cppfile. Inside theAppconstructor you will find the following entry:
SampleReactModuleis the name of the Windows Runtime Component we have previously created, thus it's the namespace that contains ourReactPackageProviderimplementation. To make this code compiling we need also to include the header file of our module at the top:
\n
\n
#include\"winrt/SampleReactModule.h\"\n
\n
This is how the wholeApp.cppfile should look like:
\n
\n
#include\"pch.h\"\n\n#include\"App.h\"\n#include\"ReactPackageProvider.h\"\n#include\"winrt/SampleReactModule.h\"\n\nusingnamespace winrt::NativeModuleSample;\nusingnamespace winrt::NativeModuleSample::implementation;\n\n/// <summary>\n/// Initializes the singleton application object. This is the first line of\n/// authored code executed, and as such is the logical equivalent of main() or\n/// WinMain().\n/// </summary>\nApp::App() noexcept\n{\n MainComponentName(L\"NativeModuleSample\");\n\n#if BUNDLE\n JavaScriptBundleFile(L\"index.windows\");\n InstanceSettings().UseWebDebugger(false);\n InstanceSettings().UseLiveReload(false);\n#else\n JavaScriptMainModuleName(L\"index\");\n InstanceSettings().UseWebDebugger(true);\n InstanceSettings().UseLiveReload(true);\n#endif\n\n#if _DEBUG\n InstanceSettings().EnableDeveloperMenu(true);\n#else\n InstanceSettings().EnableDeveloperMenu(false);\n#endif\n\n PackageProviders().Append(make<ReactPackageProvider>()); // Includes all modules in this project\n PackageProviders().Append(winrt::SampleReactModule::ReactPackageProvider());\n\n\n InitializeComponent();\n\n // This works around a cpp/winrt bug with composable/aggregable types tracked\n // by 22116519\n AddRef();\n m_inner.as<::IUnknown>()->Release();\n}
\n
\n
Using the module from JavaScript
\n
That's it! Now we can move to the React Native project to start using the module we have just built from JavaScript. Open the folder which contains your React Native project with your favorite web editor. For me, it's Visual Studio Code. For simplicity, I've implemented the component which uses the native module directly in theApp.jsfile of my React Native project, so that it will be the startup page.
\n
The first step is importing theNativeModuleobject exposed by React Native in our component:
\n
\n
import { NativeModules } from'react-native';\n
\n
Our module will be exposed through this object, by leveraging the name of the class we have decorated with the[ReactModule]attribute. In our case, it'sSampleComponent, so we can use the following code to access to theGetDeviceModule()method:
\n
\n
NativeModules.SampleComponent.getDeviceModel();\n
\n
Notice that we can reference the method with the lowercase name,getDeviceModel(), thanks to the parameter we have passed to the[ReactMethod]attribute. However, by default the methods exposed by native modules are implemented in an asynchronous way using callbacks. As such, if we want to consume thegetDeviceModel()method, we need to use the following approach:
\n
\n
getModel = () => {\n var current = this;\n NativeModules.SampleComponent.getDeviceModel(function(result) {\n current.setState({model: result});\n })\n}\n
\n
The method accepts a function, that is invoked once the asynchronous operation is completed. Inside this function we receive, as parameter, the result returned by our native method (in our case, the model of the device). In our sample, we store it inside the component's state, so that we can display it in the user interface using the JSX syntax:
\n
\n
<Text>Model: {this.state.model}</Text>\n
\n
Before calling thesetState()method, however, we need to save a reference to the main context. Inside the callback, in fact, we have moved to a different context, so we don't have access to the helpers exposed by React Native.
\n
Below you can find the full definition of our component:
If you're familiar with React Native, the code should be easy to understand:
\n\n
We have defined a function calledgetModel(), which takes care of interacting with our native module. The model of the device is stored inside the component's state, using thesetState()function provided by React Native.
\n
Therender()methods defines the UI component, using the JSX syntax. The UI is very simple:\n
\n
We have a<Button>, which invokes thegetModel()function by leveraging theonPressevent.
\n
We have a<Text>, which displays the value of themodelproperty stored inside the state.
\n
\n
\n\n
\n
Testing the code
\n
Now it's time to test the code! First, right click on the React Native project in Visual Studio and chooseDeploy. Before doing it, however, use theConfiguration Managerdropdown to make sure you're targeting the right CPU architecture. By default, in fact, the solution will target ARM, so you will have to switch to x86 or x64. If it's the first time you build it, it will take a while since C++ compilation isn't exactly fast 😃 Once it's done, open a command prompt on the folder which contains your React Native project and typeyarn start. This will launch the Metro packager, which will serve all the React Native components to your application. Once the dependency graph has been loaded, you can open the Start menu and launch the Windows application you have just deployed from Visual Studio. If you have done everything in the correct way, you will see the very simple UI we have built in our component. By pressing the button you will invoke the native module and you will see the model of your device being displayed:
\n
\n
\n
Improving the code
\n
If you compare this development experience with the one you have when you add a 3rd party module to your project using yarn or npm, you realize that it's quite different. In such a case, in fact, you don't have to deal with theNativeModulesobject; or you don't need to use callbacks to call the various methods. Let's use again the example of the popularAsyncStoragemodule. If you want to store some data in the storage, you just import a reference to theAsyncStorageobject and then you call an asynchronous method calledsetItem:
Can we achieve the same goal with the native module we have just built? The answer is yes! We just need to build a JavaScript wrapper, that will make consuming our native module more straightforward.
\n
In Visual Studio Code add a new file in your React Native project and call itSampleComponent.js. First, let's import the sameNativeModulesobject we have previously imported in the main component:
\n
\n
import { NativeModules } from'react-native';\n
\n
Now, usingJavaScript Promises, we can build a wrapper to thegetResult()method. Thanks to Promises, we can enable an easier approach to consume our asynchronous API, thanks to theasyncandawaitkeywords. If you have some C# background, this approach is similar to use theTaskCompletionSourceclass to build asynchronous operations that returns aTaskobject-
The method returns a newPromise, which requires us to fulfill two objects:
\n
\n
resolve, which is invoked when the operation has completed successfully.
\n
reject, which is invoked when the operation has failed.
\n
\n
Inside the Promise we invoke thegetDeviceModel()method, in the same way we were doing before in the component. The only difference is that, in this case, once the callback is completed we pass theresultto theresolve()method in case of success; otherwise, we pass theerrorto thereject()method.
\n
Now that we have built our wrapper, we can simplify the component we have previously built. First, remove the following line:
\n
\n
import { NativeModules } from'react-native';\n
\n
Then replace it with the following one:
\n
\n
import * as SampleComponent from'./SampleComponent'\n
\n
This import will expose the functions we have defined in our wrapper through theSampleComponentobject. Now you can change thegetModel()function simply with this code:
\n
\n
getModel = async () => {\n var model = await SampleComponent.getDeviceModel();\n this.setState( { model: model});\n}\n
\n
As you can see, thanks to Promises and theasyncandawaitkeywords, the code is simpler to write and read. We just need to mark the function asasync, then call theSampleComponent.getDeviceModel()function with theawaitprefix. Since the asynchronous operation is managed for us, we can treat it like if it's synchronous and just store in a variable the result, which will contain the device model. Since we aren't using callbacks, we can also set the state directly by callingthis.setState(). If you are a C# developer, everything should be very familiar, since it's the same async and await approach supported by C#.
\n
That's it! Now launch the application again. In this case we didn't touch the native project, so if the Metro packager was still running, we should already be seeing the updated version. Of course there won't be any difference, since we didn't change the UI or the behavior, but the code is easier to read and maintain now.
\n
\n
Exporting the module
\n
So far, we have added the native module implementation directly in the project. What if we want to create a true self-contained module, that we can install using yarn or npm like any other 3rd party module? Well, unfortunately the story isn't complete for Windows yet. By following the documentationavailable on GitHubit's easy to create the skeleton of the module. We just need to create a new React Native module project, using thecreate-react-native-moduleCLI tool, add the React Native for Windows package and then:
\n
\n\n
Include a folder calledwindowswith the Windows Runtime Component we have developed.
\n
Optionally include any JavaScript wrapper we might have created to make easier consuming the module.
\n\n
If you already have a module with iOS and Android support, you'll just need instead to follow step 1 and include the windows folder with the Windows Runtime Component in your existing project.
\n
However, regardless of your scenario, the current React Native for Windows implementation lacks an important feature calledlinking. You'll remember that, in the previous section, we had to manually edit theApp.cppfile of the React Native project to load, through theReactPackageProviderclass, our Windows Runtime Component. On Android and iOS this operation isn't needed thanks to linking. You just need to run thereact-native linkcommand: it will take care of everything for you, without needing to manually edit the Android and iOS projects. React Native 0.60 has brought linking to the next level, by introducingautomatic linking. You don't even have to run thelinkcommand anymore; just add the package to your project and React Native will take of everything else for you. The React Native implementation for iOS and Android will automatically load all the 3rd party modules that have been added to the project. This feature isn't available on Windows yet and, as such, if you create an independent module and you publish it on NPM, you will still need to manually open the React Native for Windows solution in Visual Studio and register the module in theApp.cppfile.
\n
\n
Another important information to highlight is the language choice for your module. C++ has a performance advantage over C++, since it doesn't need to pull in the whole CLR in the React Native project. As such, especially if you're planning to share your module with the community, you should seriously consider to use C++. If, instead, you're aiming to use your module only in your project and you have verified that the performance trade off doesn't heavily affect your application, it's fine to keep using C#.
\n
Publishing the application
\n
Compared to the last time I wrote about React Native for Windows, there's a big news! Now you can create a deployable version of the application: an AppX / MSIX package which is self-contained and that doesn't need the Metro packager to be up & running. This way, you can publish the application on the Microsoft Store or using one of the many supported deployment techniques (sideloading, SSCM, Intune, etc.). To achieve this goal just start the publishing process, like you would do with any Universal Windows Platform application. Right click on the React Native project in Visual Studio, choosePublish → Create app packagesand follow the wizard. Just make sure, in the package configuration step, to chooseReleaseas configuration mode. This way, all the React Native resources will be bundled together, removing the requirement of having the Metro packager up & running.
\n
\n
Wrapping up
\n
In this post we have seen how you can build native modules for React Native for Windows. This will allow us to build applications using the React stack but, at the same time, leverage native features provided by Windows, like toast notifications, access to Bluetooth APIs, file system, etc. We have also learned how we can distribute an application built with React Native, either via the Microsoft Store or one of the many techniques supported by MSIX, like sideloading or Microsoft Intune.
\n
\n
You can find the sample I've built for this poston GitHub.
In this blog post we're going to learn how we can build such a native module with Windows support, so that we can access to native Windows APIs when our React Native application is running on Windows.
","introduction":"","coverImage":null,"coverImageProperties":{"__typename":"CoverImageProperties","style":"STANDARD","titlePosition":"BOTTOM","altText":""},"currentRevision":{"__ref":"Revision:revision:1067893_4"},"latestVersion":{"__typename":"FriendlyVersion","major":"4","minor":"0"},"metrics":{"__typename":"MessageMetrics","views":11721},"visibilityScope":"PUBLIC","canonicalUrl":null,"seoTitle":null,"seoDescription":null,"placeholder":false,"originalMessageForPlaceholder":null,"contributors":{"__typename":"UserConnection","edges":[]},"nonCoAuthorContributors":{"__typename":"UserConnection","edges":[]},"coAuthors":{"__typename":"UserConnection","edges":[]},"blogMessagePolicies":{"__typename":"BlogMessagePolicies","canDoAuthoringActionsOnBlog":{"__typename":"PolicyResult","failureReason":{"__typename":"FailureReason","message":"error.lithium.policies.blog.action_can_do_authoring_action.accessDenied","key":"error.lithium.policies.blog.action_can_do_authoring_action.accessDenied","args":[]}}},"archivalData":null,"replies":{"__typename":"MessageConnection","edges":[{"__typename":"MessageEdge","cursor":"MjUuMXwyLjF8aXwxMHwxMzI6MHxpbnQsMzY1OTE5NSwzNjU5MTk1","node":{"__ref":"BlogReplyMessage:message:3659195"}},{"__typename":"MessageEdge","cursor":"MjUuMXwyLjF8aXwxMHwxMzI6MHxpbnQsMzY1OTE5NSwzNjU1NzU2","node":{"__ref":"BlogReplyMessage:message:3655756"}}],"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}},"customFields":[],"revisions({\"constraints\":{\"isPublished\":{\"eq\":true}},\"first\":1})":{"__typename":"RevisionConnection","totalCount":4}},"Conversation:conversation:1067893":{"__typename":"Conversation","id":"conversation:1067893","solved":false,"topic":{"__ref":"BlogTopicMessage:message:1067893"},"lastPostingActivityTime":"2022-10-22T06:20:52.458-07:00","lastPostTime":"2022-10-22T06:20:52.458-07:00","unreadReplyCount":2,"isSubscribed":false},"ModerationData:moderation_data:1067893":{"__typename":"ModerationData","id":"moderation_data:1067893","status":"APPROVED","rejectReason":null,"isReportedAbuse":false,"rejectUser":null,"rejectTime":null,"rejectActorType":null},"AssociatedImage:{\"url\":\"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/bS0xMDY3ODkzLTE2MTkzMGk2REE1NjAyMzE5MDFCRjFB?revision=4\"}":{"__typename":"AssociatedImage","url":"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/bS0xMDY3ODkzLTE2MTkzMGk2REE1NjAyMzE5MDFCRjFB?revision=4","title":"WindowsRuntimeComponent.png","associationType":"BODY","width":1024,"height":710,"altText":null},"AssociatedImage:{\"url\":\"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/bS0xMDY3ODkzLTE2MTkzMWkzRjU3MzhBNUQ1RkYxMzM4?revision=4\"}":{"__typename":"AssociatedImage","url":"https://techcommunity.microsoft.com/t5/s/gxcuf89792/images/bS0xMDY3ODkzLTE2MTkzMWkzRjU3MzhBNUQ1RkYxMzM4?revision=4","title":"ReactNativeApp.png","associationType":"BODY","width":603,"height":357,"altText":null},"Revision:revision:1067893_4":{"__typename":"Revision","id":"revision:1067893_4","lastEditTime":"2020-05-22T10:10:33.491-07:00"},"CachedAsset:theme:customTheme1-1744326567578":{"__typename":"CachedAsset","id":"theme:customTheme1-1744326567578","value":{"id":"customTheme1","animation":{"fast":"150ms","normal":"250ms","slow":"500ms","slowest":"750ms","function":"cubic-bezier(0.07, 0.91, 0.51, 1)","__typename":"AnimationThemeSettings"},"avatar":{"borderRadius":"50%","collections":["default"],"__typename":"AvatarThemeSettings"},"basics":{"browserIcon":{"imageAssetName":"favicon-1730836283320.png","imageLastModified":"1730836286415","__typename":"ThemeAsset"},"customerLogo":{"imageAssetName":"favicon-1730836271365.png","imageLastModified":"1730836274203","__typename":"ThemeAsset"},"maximumWidthOfPageContent":"1300px","oneColumnNarrowWidth":"800px","gridGutterWidthMd":"30px","gridGutterWidthXs":"10px","pageWidthStyle":"WIDTH_OF_BROWSER","__typename":"BasicsThemeSettings"},"buttons":{"borderRadiusSm":"3px","borderRadius":"3px","borderRadiusLg":"5px","paddingY":"5px","paddingYLg":"7px","paddingYHero":"var(--lia-bs-btn-padding-y-lg)","paddingX":"12px","paddingXLg":"16px","paddingXHero":"60px","fontStyle":"NORMAL","fontWeight":"700","textTransform":"NONE","disabledOpacity":0.5,"primaryTextColor":"var(--lia-bs-white)","primaryTextHoverColor":"var(--lia-bs-white)","primaryTextActiveColor":"var(--lia-bs-white)","primaryBgColor":"var(--lia-bs-primary)","primaryBgHoverColor":"hsl(var(--lia-bs-primary-h), var(--lia-bs-primary-s), calc(var(--lia-bs-primary-l) * 0.85))","primaryBgActiveColor":"hsl(var(--lia-bs-primary-h), var(--lia-bs-primary-s), calc(var(--lia-bs-primary-l) * 0.7))","primaryBorder":"1px solid transparent","primaryBorderHover":"1px solid transparent","primaryBorderActive":"1px solid transparent","primaryBorderFocus":"1px solid var(--lia-bs-white)","primaryBoxShadowFocus":"0 0 0 1px var(--lia-bs-primary), 0 0 0 4px hsla(var(--lia-bs-primary-h), var(--lia-bs-primary-s), var(--lia-bs-primary-l), 0.2)","secondaryTextColor":"var(--lia-bs-gray-900)","secondaryTextHoverColor":"hsl(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), calc(var(--lia-bs-gray-900-l) * 0.95))","secondaryTextActiveColor":"hsl(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), calc(var(--lia-bs-gray-900-l) * 0.9))","secondaryBgColor":"var(--lia-bs-gray-200)","secondaryBgHoverColor":"hsl(var(--lia-bs-gray-200-h), var(--lia-bs-gray-200-s), calc(var(--lia-bs-gray-200-l) * 0.96))","secondaryBgActiveColor":"hsl(var(--lia-bs-gray-200-h), var(--lia-bs-gray-200-s), calc(var(--lia-bs-gray-200-l) * 0.92))","secondaryBorder":"1px solid transparent","secondaryBorderHover":"1px solid transparent","secondaryBorderActive":"1px solid transparent","secondaryBorderFocus":"1px solid transparent","secondaryBoxShadowFocus":"0 0 0 1px var(--lia-bs-primary), 0 0 0 4px hsla(var(--lia-bs-primary-h), var(--lia-bs-primary-s), var(--lia-bs-primary-l), 0.2)","tertiaryTextColor":"var(--lia-bs-gray-900)","tertiaryTextHoverColor":"hsl(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), calc(var(--lia-bs-gray-900-l) * 0.95))","tertiaryTextActiveColor":"hsl(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), calc(var(--lia-bs-gray-900-l) * 0.9))","tertiaryBgColor":"transparent","tertiaryBgHoverColor":"transparent","tertiaryBgActiveColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.04)","tertiaryBorder":"1px solid transparent","tertiaryBorderHover":"1px solid hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.08)","tertiaryBorderActive":"1px solid transparent","tertiaryBorderFocus":"1px solid transparent","tertiaryBoxShadowFocus":"0 0 0 1px var(--lia-bs-primary), 0 0 0 4px hsla(var(--lia-bs-primary-h), var(--lia-bs-primary-s), var(--lia-bs-primary-l), 0.2)","destructiveTextColor":"var(--lia-bs-danger)","destructiveTextHoverColor":"hsl(var(--lia-bs-danger-h), var(--lia-bs-danger-s), calc(var(--lia-bs-danger-l) * 0.95))","destructiveTextActiveColor":"hsl(var(--lia-bs-danger-h), var(--lia-bs-danger-s), calc(var(--lia-bs-danger-l) * 0.9))","destructiveBgColor":"var(--lia-bs-gray-200)","destructiveBgHoverColor":"hsl(var(--lia-bs-gray-200-h), var(--lia-bs-gray-200-s), calc(var(--lia-bs-gray-200-l) * 0.96))","destructiveBgActiveColor":"hsl(var(--lia-bs-gray-200-h), var(--lia-bs-gray-200-s), calc(var(--lia-bs-gray-200-l) * 0.92))","destructiveBorder":"1px solid transparent","destructiveBorderHover":"1px solid transparent","destructiveBorderActive":"1px solid transparent","destructiveBorderFocus":"1px solid transparent","destructiveBoxShadowFocus":"0 0 0 1px var(--lia-bs-primary), 0 0 0 4px hsla(var(--lia-bs-primary-h), var(--lia-bs-primary-s), var(--lia-bs-primary-l), 0.2)","__typename":"ButtonsThemeSettings"},"border":{"color":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.08)","mainContent":"NONE","sideContent":"LIGHT","radiusSm":"3px","radius":"5px","radiusLg":"9px","radius50":"100vw","__typename":"BorderThemeSettings"},"boxShadow":{"xs":"0 0 0 1px hsla(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), var(--lia-bs-gray-900-l), 0.08), 0 3px 0 -1px hsla(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), var(--lia-bs-gray-900-l), 0.16)","sm":"0 2px 4px hsla(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), var(--lia-bs-gray-900-l), 0.12)","md":"0 5px 15px hsla(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), var(--lia-bs-gray-900-l), 0.3)","lg":"0 10px 30px hsla(var(--lia-bs-gray-900-h), var(--lia-bs-gray-900-s), var(--lia-bs-gray-900-l), 0.3)","__typename":"BoxShadowThemeSettings"},"cards":{"bgColor":"var(--lia-panel-bg-color)","borderRadius":"var(--lia-panel-border-radius)","boxShadow":"var(--lia-box-shadow-xs)","__typename":"CardsThemeSettings"},"chip":{"maxWidth":"300px","height":"30px","__typename":"ChipThemeSettings"},"coreTypes":{"defaultMessageLinkColor":"var(--lia-bs-link-color)","defaultMessageLinkDecoration":"none","defaultMessageLinkFontStyle":"NORMAL","defaultMessageLinkFontWeight":"400","defaultMessageFontStyle":"NORMAL","defaultMessageFontWeight":"400","forumColor":"#4099E2","forumFontFamily":"var(--lia-bs-font-family-base)","forumFontWeight":"var(--lia-default-message-font-weight)","forumLineHeight":"var(--lia-bs-line-height-base)","forumFontStyle":"var(--lia-default-message-font-style)","forumMessageLinkColor":"var(--lia-default-message-link-color)","forumMessageLinkDecoration":"var(--lia-default-message-link-decoration)","forumMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","forumMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","forumSolvedColor":"#148563","blogColor":"#1CBAA0","blogFontFamily":"var(--lia-bs-font-family-base)","blogFontWeight":"var(--lia-default-message-font-weight)","blogLineHeight":"1.75","blogFontStyle":"var(--lia-default-message-font-style)","blogMessageLinkColor":"var(--lia-default-message-link-color)","blogMessageLinkDecoration":"var(--lia-default-message-link-decoration)","blogMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","blogMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","tkbColor":"#4C6B90","tkbFontFamily":"var(--lia-bs-font-family-base)","tkbFontWeight":"var(--lia-default-message-font-weight)","tkbLineHeight":"1.75","tkbFontStyle":"var(--lia-default-message-font-style)","tkbMessageLinkColor":"var(--lia-default-message-link-color)","tkbMessageLinkDecoration":"var(--lia-default-message-link-decoration)","tkbMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","tkbMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","qandaColor":"#4099E2","qandaFontFamily":"var(--lia-bs-font-family-base)","qandaFontWeight":"var(--lia-default-message-font-weight)","qandaLineHeight":"var(--lia-bs-line-height-base)","qandaFontStyle":"var(--lia-default-message-link-font-style)","qandaMessageLinkColor":"var(--lia-default-message-link-color)","qandaMessageLinkDecoration":"var(--lia-default-message-link-decoration)","qandaMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","qandaMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","qandaSolvedColor":"#3FA023","ideaColor":"#FF8000","ideaFontFamily":"var(--lia-bs-font-family-base)","ideaFontWeight":"var(--lia-default-message-font-weight)","ideaLineHeight":"var(--lia-bs-line-height-base)","ideaFontStyle":"var(--lia-default-message-font-style)","ideaMessageLinkColor":"var(--lia-default-message-link-color)","ideaMessageLinkDecoration":"var(--lia-default-message-link-decoration)","ideaMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","ideaMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","contestColor":"#FCC845","contestFontFamily":"var(--lia-bs-font-family-base)","contestFontWeight":"var(--lia-default-message-font-weight)","contestLineHeight":"var(--lia-bs-line-height-base)","contestFontStyle":"var(--lia-default-message-link-font-style)","contestMessageLinkColor":"var(--lia-default-message-link-color)","contestMessageLinkDecoration":"var(--lia-default-message-link-decoration)","contestMessageLinkFontStyle":"ITALIC","contestMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","occasionColor":"#D13A1F","occasionFontFamily":"var(--lia-bs-font-family-base)","occasionFontWeight":"var(--lia-default-message-font-weight)","occasionLineHeight":"var(--lia-bs-line-height-base)","occasionFontStyle":"var(--lia-default-message-font-style)","occasionMessageLinkColor":"var(--lia-default-message-link-color)","occasionMessageLinkDecoration":"var(--lia-default-message-link-decoration)","occasionMessageLinkFontStyle":"var(--lia-default-message-link-font-style)","occasionMessageLinkFontWeight":"var(--lia-default-message-link-font-weight)","grouphubColor":"#333333","categoryColor":"#949494","communityColor":"#FFFFFF","productColor":"#949494","__typename":"CoreTypesThemeSettings"},"colors":{"black":"#000000","white":"#FFFFFF","gray100":"#F7F7F7","gray200":"#F7F7F7","gray300":"#E8E8E8","gray400":"#D9D9D9","gray500":"#CCCCCC","gray600":"#717171","gray700":"#707070","gray800":"#545454","gray900":"#333333","dark":"#545454","light":"#F7F7F7","primary":"#0069D4","secondary":"#333333","bodyText":"#1E1E1E","bodyBg":"#FFFFFF","info":"#409AE2","success":"#41C5AE","warning":"#FCC844","danger":"#BC341B","alertSystem":"#FF6600","textMuted":"#707070","highlight":"#FFFCAD","outline":"var(--lia-bs-primary)","custom":["#D3F5A4","#243A5E"],"__typename":"ColorsThemeSettings"},"divider":{"size":"3px","marginLeft":"4px","marginRight":"4px","borderRadius":"50%","bgColor":"var(--lia-bs-gray-600)","bgColorActive":"var(--lia-bs-gray-600)","__typename":"DividerThemeSettings"},"dropdown":{"fontSize":"var(--lia-bs-font-size-sm)","borderColor":"var(--lia-bs-border-color)","borderRadius":"var(--lia-bs-border-radius-sm)","dividerBg":"var(--lia-bs-gray-300)","itemPaddingY":"5px","itemPaddingX":"20px","headerColor":"var(--lia-bs-gray-700)","__typename":"DropdownThemeSettings"},"email":{"link":{"color":"#0069D4","hoverColor":"#0061c2","decoration":"none","hoverDecoration":"underline","__typename":"EmailLinkSettings"},"border":{"color":"#e4e4e4","__typename":"EmailBorderSettings"},"buttons":{"borderRadiusLg":"5px","paddingXLg":"16px","paddingYLg":"7px","fontWeight":"700","primaryTextColor":"#ffffff","primaryTextHoverColor":"#ffffff","primaryBgColor":"#0069D4","primaryBgHoverColor":"#005cb8","primaryBorder":"1px solid transparent","primaryBorderHover":"1px solid transparent","__typename":"EmailButtonsSettings"},"panel":{"borderRadius":"5px","borderColor":"#e4e4e4","__typename":"EmailPanelSettings"},"__typename":"EmailThemeSettings"},"emoji":{"skinToneDefault":"#ffcd43","skinToneLight":"#fae3c5","skinToneMediumLight":"#e2cfa5","skinToneMedium":"#daa478","skinToneMediumDark":"#a78058","skinToneDark":"#5e4d43","__typename":"EmojiThemeSettings"},"heading":{"color":"var(--lia-bs-body-color)","fontFamily":"Segoe UI","fontStyle":"NORMAL","fontWeight":"400","h1FontSize":"34px","h2FontSize":"32px","h3FontSize":"28px","h4FontSize":"24px","h5FontSize":"20px","h6FontSize":"16px","lineHeight":"1.3","subHeaderFontSize":"11px","subHeaderFontWeight":"500","h1LetterSpacing":"normal","h2LetterSpacing":"normal","h3LetterSpacing":"normal","h4LetterSpacing":"normal","h5LetterSpacing":"normal","h6LetterSpacing":"normal","subHeaderLetterSpacing":"2px","h1FontWeight":"var(--lia-bs-headings-font-weight)","h2FontWeight":"var(--lia-bs-headings-font-weight)","h3FontWeight":"var(--lia-bs-headings-font-weight)","h4FontWeight":"var(--lia-bs-headings-font-weight)","h5FontWeight":"var(--lia-bs-headings-font-weight)","h6FontWeight":"var(--lia-bs-headings-font-weight)","__typename":"HeadingThemeSettings"},"icons":{"size10":"10px","size12":"12px","size14":"14px","size16":"16px","size20":"20px","size24":"24px","size30":"30px","size40":"40px","size50":"50px","size60":"60px","size80":"80px","size120":"120px","size160":"160px","__typename":"IconsThemeSettings"},"imagePreview":{"bgColor":"var(--lia-bs-gray-900)","titleColor":"var(--lia-bs-white)","controlColor":"var(--lia-bs-white)","controlBgColor":"var(--lia-bs-gray-800)","__typename":"ImagePreviewThemeSettings"},"input":{"borderColor":"var(--lia-bs-gray-600)","disabledColor":"var(--lia-bs-gray-600)","focusBorderColor":"var(--lia-bs-primary)","labelMarginBottom":"10px","btnFontSize":"var(--lia-bs-font-size-sm)","focusBoxShadow":"0 0 0 3px hsla(var(--lia-bs-primary-h), var(--lia-bs-primary-s), var(--lia-bs-primary-l), 0.2)","checkLabelMarginBottom":"2px","checkboxBorderRadius":"3px","borderRadiusSm":"var(--lia-bs-border-radius-sm)","borderRadius":"var(--lia-bs-border-radius)","borderRadiusLg":"var(--lia-bs-border-radius-lg)","formTextMarginTop":"4px","textAreaBorderRadius":"var(--lia-bs-border-radius)","activeFillColor":"var(--lia-bs-primary)","__typename":"InputThemeSettings"},"loading":{"dotDarkColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.2)","dotLightColor":"hsla(var(--lia-bs-white-h), var(--lia-bs-white-s), var(--lia-bs-white-l), 0.5)","barDarkColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.06)","barLightColor":"hsla(var(--lia-bs-white-h), var(--lia-bs-white-s), var(--lia-bs-white-l), 0.4)","__typename":"LoadingThemeSettings"},"link":{"color":"var(--lia-bs-primary)","hoverColor":"hsl(var(--lia-bs-primary-h), var(--lia-bs-primary-s), calc(var(--lia-bs-primary-l) - 10%))","decoration":"none","hoverDecoration":"underline","__typename":"LinkThemeSettings"},"listGroup":{"itemPaddingY":"15px","itemPaddingX":"15px","borderColor":"var(--lia-bs-gray-300)","__typename":"ListGroupThemeSettings"},"modal":{"contentTextColor":"var(--lia-bs-body-color)","contentBg":"var(--lia-bs-white)","backgroundBg":"var(--lia-bs-black)","smSize":"440px","mdSize":"760px","lgSize":"1080px","backdropOpacity":0.3,"contentBoxShadowXs":"var(--lia-bs-box-shadow-sm)","contentBoxShadow":"var(--lia-bs-box-shadow)","headerFontWeight":"700","__typename":"ModalThemeSettings"},"navbar":{"position":"FIXED","background":{"attachment":null,"clip":null,"color":"var(--lia-bs-white)","imageAssetName":"","imageLastModified":"0","origin":null,"position":"CENTER_CENTER","repeat":"NO_REPEAT","size":"COVER","__typename":"BackgroundProps"},"backgroundOpacity":0.8,"paddingTop":"15px","paddingBottom":"15px","borderBottom":"1px solid var(--lia-bs-border-color)","boxShadow":"var(--lia-bs-box-shadow-sm)","brandMarginRight":"30px","brandMarginRightSm":"10px","brandLogoHeight":"30px","linkGap":"10px","linkJustifyContent":"flex-start","linkPaddingY":"5px","linkPaddingX":"10px","linkDropdownPaddingY":"9px","linkDropdownPaddingX":"var(--lia-nav-link-px)","linkColor":"var(--lia-bs-body-color)","linkHoverColor":"var(--lia-bs-primary)","linkFontSize":"var(--lia-bs-font-size-sm)","linkFontStyle":"NORMAL","linkFontWeight":"400","linkTextTransform":"NONE","linkLetterSpacing":"normal","linkBorderRadius":"var(--lia-bs-border-radius-sm)","linkBgColor":"transparent","linkBgHoverColor":"transparent","linkBorder":"none","linkBorderHover":"none","linkBoxShadow":"none","linkBoxShadowHover":"none","linkTextBorderBottom":"none","linkTextBorderBottomHover":"none","dropdownPaddingTop":"10px","dropdownPaddingBottom":"15px","dropdownPaddingX":"10px","dropdownMenuOffset":"2px","dropdownDividerMarginTop":"10px","dropdownDividerMarginBottom":"10px","dropdownBorderColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.08)","controllerBgHoverColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.1)","controllerIconColor":"var(--lia-bs-body-color)","controllerIconHoverColor":"var(--lia-bs-body-color)","controllerTextColor":"var(--lia-nav-controller-icon-color)","controllerTextHoverColor":"var(--lia-nav-controller-icon-hover-color)","controllerHighlightColor":"hsla(30, 100%, 50%)","controllerHighlightTextColor":"var(--lia-yiq-light)","controllerBorderRadius":"var(--lia-border-radius-50)","hamburgerColor":"var(--lia-nav-controller-icon-color)","hamburgerHoverColor":"var(--lia-nav-controller-icon-color)","hamburgerBgColor":"transparent","hamburgerBgHoverColor":"transparent","hamburgerBorder":"none","hamburgerBorderHover":"none","collapseMenuMarginLeft":"20px","collapseMenuDividerBg":"var(--lia-nav-link-color)","collapseMenuDividerOpacity":0.16,"__typename":"NavbarThemeSettings"},"pager":{"textColor":"var(--lia-bs-link-color)","textFontWeight":"var(--lia-font-weight-md)","textFontSize":"var(--lia-bs-font-size-sm)","__typename":"PagerThemeSettings"},"panel":{"bgColor":"var(--lia-bs-white)","borderRadius":"var(--lia-bs-border-radius)","borderColor":"var(--lia-bs-border-color)","boxShadow":"none","__typename":"PanelThemeSettings"},"popover":{"arrowHeight":"8px","arrowWidth":"16px","maxWidth":"300px","minWidth":"100px","headerBg":"var(--lia-bs-white)","borderColor":"var(--lia-bs-border-color)","borderRadius":"var(--lia-bs-border-radius)","boxShadow":"0 0.5rem 1rem hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.15)","__typename":"PopoverThemeSettings"},"prism":{"color":"#000000","bgColor":"#f5f2f0","fontFamily":"var(--font-family-monospace)","fontSize":"var(--lia-bs-font-size-base)","fontWeightBold":"var(--lia-bs-font-weight-bold)","fontStyleItalic":"italic","tabSize":2,"highlightColor":"#b3d4fc","commentColor":"#62707e","punctuationColor":"#6f6f6f","namespaceOpacity":"0.7","propColor":"#990055","selectorColor":"#517a00","operatorColor":"#906736","operatorBgColor":"hsla(0, 0%, 100%, 0.5)","keywordColor":"#0076a9","functionColor":"#d3284b","variableColor":"#c14700","__typename":"PrismThemeSettings"},"rte":{"bgColor":"var(--lia-bs-white)","borderRadius":"var(--lia-panel-border-radius)","boxShadow":" var(--lia-panel-box-shadow)","customColor1":"#bfedd2","customColor2":"#fbeeb8","customColor3":"#f8cac6","customColor4":"#eccafa","customColor5":"#c2e0f4","customColor6":"#2dc26b","customColor7":"#f1c40f","customColor8":"#e03e2d","customColor9":"#b96ad9","customColor10":"#3598db","customColor11":"#169179","customColor12":"#e67e23","customColor13":"#ba372a","customColor14":"#843fa1","customColor15":"#236fa1","customColor16":"#ecf0f1","customColor17":"#ced4d9","customColor18":"#95a5a6","customColor19":"#7e8c8d","customColor20":"#34495e","customColor21":"#000000","customColor22":"#ffffff","defaultMessageHeaderMarginTop":"40px","defaultMessageHeaderMarginBottom":"20px","defaultMessageItemMarginTop":"0","defaultMessageItemMarginBottom":"10px","diffAddedColor":"hsla(170, 53%, 51%, 0.4)","diffChangedColor":"hsla(43, 97%, 63%, 0.4)","diffNoneColor":"hsla(0, 0%, 80%, 0.4)","diffRemovedColor":"hsla(9, 74%, 47%, 0.4)","specialMessageHeaderMarginTop":"40px","specialMessageHeaderMarginBottom":"20px","specialMessageItemMarginTop":"0","specialMessageItemMarginBottom":"10px","__typename":"RteThemeSettings"},"tags":{"bgColor":"var(--lia-bs-gray-200)","bgHoverColor":"var(--lia-bs-gray-400)","borderRadius":"var(--lia-bs-border-radius-sm)","color":"var(--lia-bs-body-color)","hoverColor":"var(--lia-bs-body-color)","fontWeight":"var(--lia-font-weight-md)","fontSize":"var(--lia-font-size-xxs)","textTransform":"UPPERCASE","letterSpacing":"0.5px","__typename":"TagsThemeSettings"},"toasts":{"borderRadius":"var(--lia-bs-border-radius)","paddingX":"12px","__typename":"ToastsThemeSettings"},"typography":{"fontFamilyBase":"Segoe UI","fontStyleBase":"NORMAL","fontWeightBase":"400","fontWeightLight":"300","fontWeightNormal":"400","fontWeightMd":"500","fontWeightBold":"700","letterSpacingSm":"normal","letterSpacingXs":"normal","lineHeightBase":"1.5","fontSizeBase":"16px","fontSizeXxs":"11px","fontSizeXs":"12px","fontSizeSm":"14px","fontSizeLg":"20px","fontSizeXl":"24px","smallFontSize":"14px","customFonts":[{"source":"SERVER","name":"Segoe UI","styles":[{"style":"NORMAL","weight":"400","__typename":"FontStyleData"},{"style":"NORMAL","weight":"300","__typename":"FontStyleData"},{"style":"NORMAL","weight":"600","__typename":"FontStyleData"},{"style":"NORMAL","weight":"700","__typename":"FontStyleData"},{"style":"ITALIC","weight":"400","__typename":"FontStyleData"}],"assetNames":["SegoeUI-normal-400.woff2","SegoeUI-normal-300.woff2","SegoeUI-normal-600.woff2","SegoeUI-normal-700.woff2","SegoeUI-italic-400.woff2"],"__typename":"CustomFont"},{"source":"SERVER","name":"MWF Fluent Icons","styles":[{"style":"NORMAL","weight":"400","__typename":"FontStyleData"}],"assetNames":["MWFFluentIcons-normal-400.woff2"],"__typename":"CustomFont"}],"__typename":"TypographyThemeSettings"},"unstyledListItem":{"marginBottomSm":"5px","marginBottomMd":"10px","marginBottomLg":"15px","marginBottomXl":"20px","marginBottomXxl":"25px","__typename":"UnstyledListItemThemeSettings"},"yiq":{"light":"#ffffff","dark":"#000000","__typename":"YiqThemeSettings"},"colorLightness":{"primaryDark":0.36,"primaryLight":0.74,"primaryLighter":0.89,"primaryLightest":0.95,"infoDark":0.39,"infoLight":0.72,"infoLighter":0.85,"infoLightest":0.93,"successDark":0.24,"successLight":0.62,"successLighter":0.8,"successLightest":0.91,"warningDark":0.39,"warningLight":0.68,"warningLighter":0.84,"warningLightest":0.93,"dangerDark":0.41,"dangerLight":0.72,"dangerLighter":0.89,"dangerLightest":0.95,"__typename":"ColorLightnessThemeSettings"},"localOverride":false,"__typename":"Theme"},"localOverride":false},"CachedAsset:text:en_US-components/common/EmailVerification-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/common/EmailVerification-1745505310960","value":{"email.verification.title":"Email Verification Required","email.verification.message.update.email":"To participate in the community, you must first verify your email address. The verification email was sent to {email}. To change your email, visit My Settings.","email.verification.message.resend.email":"To participate in the community, you must first verify your email address. The verification email was sent to {email}. Resend email."},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/common/Loading/LoadingDot-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/common/Loading/LoadingDot-1745505310960","value":{"title":"Loading..."},"localOverride":false},"CachedAsset:quilt:o365.prod:pages/blogs/BlogMessagePage:board:ModernWorkAppConsult-1745502712788":{"__typename":"CachedAsset","id":"quilt:o365.prod:pages/blogs/BlogMessagePage:board:ModernWorkAppConsult-1745502712788","value":{"id":"BlogMessagePage","container":{"id":"Common","headerProps":{"backgroundImageProps":null,"backgroundColor":null,"addComponents":null,"removeComponents":["community.widget.bannerWidget"],"componentOrder":null,"__typename":"QuiltContainerSectionProps"},"headerComponentProps":{"community.widget.breadcrumbWidget":{"disableLastCrumbForDesktop":false}},"footerProps":null,"footerComponentProps":null,"items":[{"id":"blog-article","layout":"ONE_COLUMN","bgColor":null,"showTitle":null,"showDescription":null,"textPosition":null,"textColor":null,"sectionEditLevel":"LOCKED","bgImage":null,"disableSpacing":null,"edgeToEdgeDisplay":null,"fullHeight":null,"showBorder":null,"__typename":"OneColumnQuiltSection","columnMap":{"main":[{"id":"blogs.widget.blogArticleWidget","className":"lia-blog-container","props":null,"__typename":"QuiltComponent"}],"__typename":"OneSectionColumns"}},{"id":"section-1729184836777","layout":"MAIN_SIDE","bgColor":"transparent","showTitle":false,"showDescription":false,"textPosition":"CENTER","textColor":"var(--lia-bs-body-color)","sectionEditLevel":null,"bgImage":null,"disableSpacing":null,"edgeToEdgeDisplay":null,"fullHeight":null,"showBorder":null,"__typename":"MainSideQuiltSection","columnMap":{"main":[],"side":[],"__typename":"MainSideSectionColumns"}}],"__typename":"QuiltContainer"},"__typename":"Quilt","localOverride":false},"localOverride":false},"CachedAsset:text:en_US-pages/blogs/BlogMessagePage-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-pages/blogs/BlogMessagePage-1745505310960","value":{"title":"{contextMessageSubject} | {communityTitle}","errorMissing":"This blog post cannot be found","name":"Blog Message Page","section.blog-article.title":"Blog Post","archivedMessageTitle":"This Content Has Been Archived","section.section-1729184836777.title":"","section.section-1729184836777.description":"","section.CncIde.title":"Blog Post","section.tifEmD.description":"","section.tifEmD.title":""},"localOverride":false},"CachedAsset:quiltWrapper:o365.prod:Common:1745505311062":{"__typename":"CachedAsset","id":"quiltWrapper:o365.prod:Common:1745505311062","value":{"id":"Common","header":{"backgroundImageProps":{"assetName":null,"backgroundSize":"COVER","backgroundRepeat":"NO_REPEAT","backgroundPosition":"CENTER_CENTER","lastModified":null,"__typename":"BackgroundImageProps"},"backgroundColor":"transparent","items":[{"id":"community.widget.navbarWidget","props":{"showUserName":true,"showRegisterLink":true,"useIconLanguagePicker":true,"useLabelLanguagePicker":true,"className":"QuiltComponent_lia-component-edit-mode__0nCcm","links":{"sideLinks":[],"mainLinks":[{"children":[],"linkType":"INTERNAL","id":"gxcuf89792","params":{},"routeName":"CommunityPage"},{"children":[],"linkType":"EXTERNAL","id":"external-link","url":"/Directory","target":"SELF"},{"children":[{"linkType":"INTERNAL","id":"microsoft365","params":{"categoryId":"microsoft365"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"windows","params":{"categoryId":"Windows"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"Common-microsoft365-copilot-link","params":{"categoryId":"Microsoft365Copilot"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"microsoft-teams","params":{"categoryId":"MicrosoftTeams"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"microsoft-securityand-compliance","params":{"categoryId":"microsoft-security"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"azure","params":{"categoryId":"Azure"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"Common-content_management-link","params":{"categoryId":"Content_Management"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"exchange","params":{"categoryId":"Exchange"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"windows-server","params":{"categoryId":"Windows-Server"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"outlook","params":{"categoryId":"Outlook"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"microsoft-endpoint-manager","params":{"categoryId":"microsoftintune"},"routeName":"CategoryPage"},{"linkType":"EXTERNAL","id":"external-link-2","url":"/Directory","target":"SELF"}],"linkType":"EXTERNAL","id":"communities","url":"/","target":"BLANK"},{"children":[{"linkType":"INTERNAL","id":"a-i","params":{"categoryId":"AI"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"education-sector","params":{"categoryId":"EducationSector"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"partner-community","params":{"categoryId":"PartnerCommunity"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"i-t-ops-talk","params":{"categoryId":"ITOpsTalk"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"healthcare-and-life-sciences","params":{"categoryId":"HealthcareAndLifeSciences"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"microsoft-mechanics","params":{"categoryId":"MicrosoftMechanics"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"public-sector","params":{"categoryId":"PublicSector"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"s-m-b","params":{"categoryId":"MicrosoftforNonprofits"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"io-t","params":{"categoryId":"IoT"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"startupsat-microsoft","params":{"categoryId":"StartupsatMicrosoft"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"driving-adoption","params":{"categoryId":"DrivingAdoption"},"routeName":"CategoryPage"},{"linkType":"EXTERNAL","id":"external-link-1","url":"/Directory","target":"SELF"}],"linkType":"EXTERNAL","id":"communities-1","url":"/","target":"SELF"},{"children":[],"linkType":"EXTERNAL","id":"external","url":"/Blogs","target":"SELF"},{"children":[],"linkType":"EXTERNAL","id":"external-1","url":"/Events","target":"SELF"},{"children":[{"linkType":"INTERNAL","id":"microsoft-learn-1","params":{"categoryId":"MicrosoftLearn"},"routeName":"CategoryPage"},{"linkType":"INTERNAL","id":"microsoft-learn-blog","params":{"boardId":"MicrosoftLearnBlog","categoryId":"MicrosoftLearn"},"routeName":"BlogBoardPage"},{"linkType":"EXTERNAL","id":"external-10","url":"https://learningroomdirectory.microsoft.com/","target":"BLANK"},{"linkType":"EXTERNAL","id":"external-3","url":"https://docs.microsoft.com/learn/dynamics365/?WT.mc_id=techcom_header-webpage-m365","target":"BLANK"},{"linkType":"EXTERNAL","id":"external-4","url":"https://docs.microsoft.com/learn/m365/?wt.mc_id=techcom_header-webpage-m365","target":"BLANK"},{"linkType":"EXTERNAL","id":"external-5","url":"https://docs.microsoft.com/learn/topics/sci/?wt.mc_id=techcom_header-webpage-m365","target":"BLANK"},{"linkType":"EXTERNAL","id":"external-6","url":"https://docs.microsoft.com/learn/powerplatform/?wt.mc_id=techcom_header-webpage-powerplatform","target":"BLANK"},{"linkType":"EXTERNAL","id":"external-7","url":"https://docs.microsoft.com/learn/github/?wt.mc_id=techcom_header-webpage-github","target":"BLANK"},{"linkType":"EXTERNAL","id":"external-8","url":"https://docs.microsoft.com/learn/teams/?wt.mc_id=techcom_header-webpage-teams","target":"BLANK"},{"linkType":"EXTERNAL","id":"external-9","url":"https://docs.microsoft.com/learn/dotnet/?wt.mc_id=techcom_header-webpage-dotnet","target":"BLANK"},{"linkType":"EXTERNAL","id":"external-2","url":"https://docs.microsoft.com/learn/azure/?WT.mc_id=techcom_header-webpage-m365","target":"BLANK"}],"linkType":"INTERNAL","id":"microsoft-learn","params":{"categoryId":"MicrosoftLearn"},"routeName":"CategoryPage"},{"children":[],"linkType":"INTERNAL","id":"community-info-center","params":{"categoryId":"Community-Info-Center"},"routeName":"CategoryPage"}]},"style":{"boxShadow":"var(--lia-bs-box-shadow-sm)","controllerHighlightColor":"hsla(30, 100%, 50%)","linkFontWeight":"400","dropdownDividerMarginBottom":"10px","hamburgerBorderHover":"none","linkBoxShadowHover":"none","linkFontSize":"14px","backgroundOpacity":0.8,"controllerBorderRadius":"var(--lia-border-radius-50)","hamburgerBgColor":"transparent","hamburgerColor":"var(--lia-nav-controller-icon-color)","linkTextBorderBottom":"none","brandLogoHeight":"30px","linkBgHoverColor":"transparent","linkLetterSpacing":"normal","collapseMenuDividerOpacity":0.16,"dropdownPaddingBottom":"15px","paddingBottom":"15px","dropdownMenuOffset":"2px","hamburgerBgHoverColor":"transparent","borderBottom":"1px solid var(--lia-bs-border-color)","hamburgerBorder":"none","dropdownPaddingX":"10px","brandMarginRightSm":"10px","linkBoxShadow":"none","collapseMenuDividerBg":"var(--lia-nav-link-color)","linkColor":"var(--lia-bs-body-color)","linkJustifyContent":"flex-start","dropdownPaddingTop":"10px","controllerHighlightTextColor":"var(--lia-yiq-dark)","controllerTextColor":"var(--lia-nav-controller-icon-color)","background":{"imageAssetName":"","color":"var(--lia-bs-white)","size":"COVER","repeat":"NO_REPEAT","position":"CENTER_CENTER","imageLastModified":""},"linkBorderRadius":"var(--lia-bs-border-radius-sm)","linkHoverColor":"var(--lia-bs-body-color)","position":"FIXED","linkBorder":"none","linkTextBorderBottomHover":"2px solid var(--lia-bs-body-color)","brandMarginRight":"30px","hamburgerHoverColor":"var(--lia-nav-controller-icon-color)","linkBorderHover":"none","collapseMenuMarginLeft":"20px","linkFontStyle":"NORMAL","controllerTextHoverColor":"var(--lia-nav-controller-icon-hover-color)","linkPaddingX":"10px","linkPaddingY":"5px","paddingTop":"15px","linkTextTransform":"NONE","dropdownBorderColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.08)","controllerBgHoverColor":"hsla(var(--lia-bs-black-h), var(--lia-bs-black-s), var(--lia-bs-black-l), 0.1)","linkBgColor":"transparent","linkDropdownPaddingX":"var(--lia-nav-link-px)","linkDropdownPaddingY":"9px","controllerIconColor":"var(--lia-bs-body-color)","dropdownDividerMarginTop":"10px","linkGap":"10px","controllerIconHoverColor":"var(--lia-bs-body-color)"},"showSearchIcon":false,"languagePickerStyle":"iconAndLabel"},"__typename":"QuiltComponent"},{"id":"community.widget.breadcrumbWidget","props":{"backgroundColor":"transparent","linkHighlightColor":"var(--lia-bs-primary)","visualEffects":{"showBottomBorder":true},"linkTextColor":"var(--lia-bs-gray-700)"},"__typename":"QuiltComponent"},{"id":"custom.widget.community_banner","props":{"widgetVisibility":"signedInOrAnonymous","useTitle":true,"usePageWidth":false,"useBackground":false,"title":"","lazyLoad":false},"__typename":"QuiltComponent"},{"id":"custom.widget.HeroBanner","props":{"widgetVisibility":"signedInOrAnonymous","usePageWidth":false,"useTitle":true,"cMax_items":3,"useBackground":false,"title":"","lazyLoad":false,"widgetChooser":"custom.widget.HeroBanner"},"__typename":"QuiltComponent"}],"__typename":"QuiltWrapperSection"},"footer":{"backgroundImageProps":{"assetName":null,"backgroundSize":"COVER","backgroundRepeat":"NO_REPEAT","backgroundPosition":"CENTER_CENTER","lastModified":null,"__typename":"BackgroundImageProps"},"backgroundColor":"transparent","items":[{"id":"custom.widget.MicrosoftFooter","props":{"widgetVisibility":"signedInOrAnonymous","useTitle":true,"useBackground":false,"title":"","lazyLoad":false},"__typename":"QuiltComponent"}],"__typename":"QuiltWrapperSection"},"__typename":"QuiltWrapper","localOverride":false},"localOverride":false},"CachedAsset:text:en_US-components/common/ActionFeedback-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/common/ActionFeedback-1745505310960","value":{"joinedGroupHub.title":"Welcome","joinedGroupHub.message":"You are now a member of this group and are subscribed to updates.","groupHubInviteNotFound.title":"Invitation Not Found","groupHubInviteNotFound.message":"Sorry, we could not find your invitation to the group. The owner may have canceled the invite.","groupHubNotFound.title":"Group Not Found","groupHubNotFound.message":"The grouphub you tried to join does not exist. It may have been deleted.","existingGroupHubMember.title":"Already Joined","existingGroupHubMember.message":"You are already a member of this group.","accountLocked.title":"Account Locked","accountLocked.message":"Your account has been locked due to multiple failed attempts. Try again in {lockoutTime} minutes.","editedGroupHub.title":"Changes Saved","editedGroupHub.message":"Your group has been updated.","leftGroupHub.title":"Goodbye","leftGroupHub.message":"You are no longer a member of this group and will not receive future updates.","deletedGroupHub.title":"Deleted","deletedGroupHub.message":"The group has been deleted.","groupHubCreated.title":"Group Created","groupHubCreated.message":"{groupHubName} is ready to use","accountClosed.title":"Account Closed","accountClosed.message":"The account has been closed and you will now be redirected to the homepage","resetTokenExpired.title":"Reset Password Link has Expired","resetTokenExpired.message":"Try resetting your password again","invalidUrl.title":"Invalid URL","invalidUrl.message":"The URL you're using is not recognized. Verify your URL and try again.","accountClosedForUser.title":"Account Closed","accountClosedForUser.message":"{userName}'s account is closed","inviteTokenInvalid.title":"Invitation Invalid","inviteTokenInvalid.message":"Your invitation to the community has been canceled or expired.","inviteTokenError.title":"Invitation Verification Failed","inviteTokenError.message":"The url you are utilizing is not recognized. Verify your URL and try again","pageNotFound.title":"Access Denied","pageNotFound.message":"You do not have access to this area of the community or it doesn't exist","eventAttending.title":"Responded as Attending","eventAttending.message":"You'll be notified when there's new activity and reminded as the event approaches","eventInterested.title":"Responded as Interested","eventInterested.message":"You'll be notified when there's new activity and reminded as the event approaches","eventNotFound.title":"Event Not Found","eventNotFound.message":"The event you tried to respond to does not exist.","redirectToRelatedPage.title":"Showing Related Content","redirectToRelatedPageForBaseUsers.title":"Showing Related Content","redirectToRelatedPageForBaseUsers.message":"The content you are trying to access is archived","redirectToRelatedPage.message":"The content you are trying to access is archived","relatedUrl.archivalLink.flyoutMessage":"The content you are trying to access is archived View Archived Content"},"localOverride":false},"CachedAsset:component:custom.widget.community_banner-en-1744400828040":{"__typename":"CachedAsset","id":"component:custom.widget.community_banner-en-1744400828040","value":{"component":{"id":"custom.widget.community_banner","template":{"id":"community_banner","markupLanguage":"HANDLEBARS","style":".community-banner {\n a.top-bar.btn {\n top: 0px;\n width: 100%;\n z-index: 999;\n text-align: center;\n left: 0px;\n background: #0068b8;\n color: white;\n padding: 10px 0px;\n display: block;\n box-shadow: none !important;\n border: none !important;\n border-radius: none !important;\n margin: 0px !important;\n font-size: 14px;\n }\n}\n","texts":null,"defaults":{"config":{"applicablePages":[],"description":"community announcement text","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"components":[{"id":"custom.widget.community_banner","form":null,"config":null,"props":[],"__typename":"Component"}],"grouping":"CUSTOM","__typename":"ComponentTemplate"},"properties":{"config":{"applicablePages":[],"description":"community announcement text","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"form":null,"__typename":"Component","localOverride":false},"globalCss":{"css":".custom_widget_community_banner_community-banner_1x9u2_1 {\n a.custom_widget_community_banner_top-bar_1x9u2_2.custom_widget_community_banner_btn_1x9u2_2 {\n top: 0;\n width: 100%;\n z-index: 999;\n text-align: center;\n left: 0;\n background: #0068b8;\n color: white;\n padding: 0.625rem 0;\n display: block;\n box-shadow: none !important;\n border: none !important;\n border-radius: none !important;\n margin: 0 !important;\n font-size: 0.875rem;\n }\n}\n","tokens":{"community-banner":"custom_widget_community_banner_community-banner_1x9u2_1","top-bar":"custom_widget_community_banner_top-bar_1x9u2_2","btn":"custom_widget_community_banner_btn_1x9u2_2"}},"form":null},"localOverride":false},"CachedAsset:component:custom.widget.HeroBanner-en-1744400828040":{"__typename":"CachedAsset","id":"component:custom.widget.HeroBanner-en-1744400828040","value":{"component":{"id":"custom.widget.HeroBanner","template":{"id":"HeroBanner","markupLanguage":"REACT","style":null,"texts":{"searchPlaceholderText":"Search this community","followActionText":"Follow","unfollowActionText":"Following","searchOnHoverText":"Please enter your search term(s) and then press return key to complete a search.","blogs.sidebar.pagetitle":"Latest Blogs | Microsoft Tech Community","followThisNode":"Follow this node","unfollowThisNode":"Unfollow this node"},"defaults":{"config":{"applicablePages":[],"description":null,"fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[{"id":"max_items","dataType":"NUMBER","list":false,"defaultValue":"3","label":"Max Items","description":"The maximum number of items to display in the carousel","possibleValues":null,"control":"INPUT","__typename":"PropDefinition"}],"__typename":"ComponentProperties"},"components":[{"id":"custom.widget.HeroBanner","form":{"fields":[{"id":"widgetChooser","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"title","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"useTitle","validation":null,"noValidation":null,"dataType":"BOOLEAN","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"useBackground","validation":null,"noValidation":null,"dataType":"BOOLEAN","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"widgetVisibility","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"moreOptions","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"cMax_items","validation":null,"noValidation":null,"dataType":"NUMBER","list":false,"control":"INPUT","defaultValue":"3","label":"Max Items","description":"The maximum number of items to display in the carousel","possibleValues":null,"__typename":"FormField"}],"layout":{"rows":[{"id":"widgetChooserGroup","type":"fieldset","as":null,"items":[{"id":"widgetChooser","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"titleGroup","type":"fieldset","as":null,"items":[{"id":"title","className":null,"__typename":"FormFieldRef"},{"id":"useTitle","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"useBackground","type":"fieldset","as":null,"items":[{"id":"useBackground","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"widgetVisibility","type":"fieldset","as":null,"items":[{"id":"widgetVisibility","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"moreOptionsGroup","type":"fieldset","as":null,"items":[{"id":"moreOptions","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"componentPropsGroup","type":"fieldset","as":null,"items":[{"id":"cMax_items","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"}],"actionButtons":null,"className":"custom_widget_HeroBanner_form","formGroupFieldSeparator":"divider","__typename":"FormLayout"},"__typename":"Form"},"config":null,"props":[],"__typename":"Component"}],"grouping":"CUSTOM","__typename":"ComponentTemplate"},"properties":{"config":{"applicablePages":[],"description":null,"fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[{"id":"max_items","dataType":"NUMBER","list":false,"defaultValue":"3","label":"Max Items","description":"The maximum number of items to display in the carousel","possibleValues":null,"control":"INPUT","__typename":"PropDefinition"}],"__typename":"ComponentProperties"},"form":{"fields":[{"id":"widgetChooser","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"title","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"useTitle","validation":null,"noValidation":null,"dataType":"BOOLEAN","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"useBackground","validation":null,"noValidation":null,"dataType":"BOOLEAN","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"widgetVisibility","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"moreOptions","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"cMax_items","validation":null,"noValidation":null,"dataType":"NUMBER","list":false,"control":"INPUT","defaultValue":"3","label":"Max Items","description":"The maximum number of items to display in the carousel","possibleValues":null,"__typename":"FormField"}],"layout":{"rows":[{"id":"widgetChooserGroup","type":"fieldset","as":null,"items":[{"id":"widgetChooser","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"titleGroup","type":"fieldset","as":null,"items":[{"id":"title","className":null,"__typename":"FormFieldRef"},{"id":"useTitle","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"useBackground","type":"fieldset","as":null,"items":[{"id":"useBackground","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"widgetVisibility","type":"fieldset","as":null,"items":[{"id":"widgetVisibility","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"moreOptionsGroup","type":"fieldset","as":null,"items":[{"id":"moreOptions","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"componentPropsGroup","type":"fieldset","as":null,"items":[{"id":"cMax_items","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"}],"actionButtons":null,"className":"custom_widget_HeroBanner_form","formGroupFieldSeparator":"divider","__typename":"FormLayout"},"__typename":"Form"},"__typename":"Component","localOverride":false},"globalCss":null,"form":{"fields":[{"id":"widgetChooser","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"title","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"useTitle","validation":null,"noValidation":null,"dataType":"BOOLEAN","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"useBackground","validation":null,"noValidation":null,"dataType":"BOOLEAN","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"widgetVisibility","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"moreOptions","validation":null,"noValidation":null,"dataType":"STRING","list":null,"control":null,"defaultValue":null,"label":null,"description":null,"possibleValues":null,"__typename":"FormField"},{"id":"cMax_items","validation":null,"noValidation":null,"dataType":"NUMBER","list":false,"control":"INPUT","defaultValue":"3","label":"Max Items","description":"The maximum number of items to display in the carousel","possibleValues":null,"__typename":"FormField"}],"layout":{"rows":[{"id":"widgetChooserGroup","type":"fieldset","as":null,"items":[{"id":"widgetChooser","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"titleGroup","type":"fieldset","as":null,"items":[{"id":"title","className":null,"__typename":"FormFieldRef"},{"id":"useTitle","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"useBackground","type":"fieldset","as":null,"items":[{"id":"useBackground","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"widgetVisibility","type":"fieldset","as":null,"items":[{"id":"widgetVisibility","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"moreOptionsGroup","type":"fieldset","as":null,"items":[{"id":"moreOptions","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"},{"id":"componentPropsGroup","type":"fieldset","as":null,"items":[{"id":"cMax_items","className":null,"__typename":"FormFieldRef"}],"props":null,"legend":null,"description":null,"className":null,"viewVariant":null,"toggleState":null,"__typename":"FormFieldset"}],"actionButtons":null,"className":"custom_widget_HeroBanner_form","formGroupFieldSeparator":"divider","__typename":"FormLayout"},"__typename":"Form"}},"localOverride":false},"CachedAsset:component:custom.widget.MicrosoftFooter-en-1744400828040":{"__typename":"CachedAsset","id":"component:custom.widget.MicrosoftFooter-en-1744400828040","value":{"component":{"id":"custom.widget.MicrosoftFooter","template":{"id":"MicrosoftFooter","markupLanguage":"HANDLEBARS","style":".context-uhf {\n min-width: 280px;\n font-size: 15px;\n box-sizing: border-box;\n -ms-text-size-adjust: 100%;\n -webkit-text-size-adjust: 100%;\n & *,\n & *:before,\n & *:after {\n box-sizing: inherit;\n }\n a.c-uhff-link {\n color: #616161;\n word-break: break-word;\n text-decoration: none;\n }\n &a:link,\n &a:focus,\n &a:hover,\n &a:active,\n &a:visited {\n text-decoration: none;\n color: inherit;\n }\n & div {\n font-family: 'Segoe UI', SegoeUI, 'Helvetica Neue', Helvetica, Arial, sans-serif;\n }\n}\n.c-uhff {\n background: #f2f2f2;\n margin: -1.5625;\n width: auto;\n height: auto;\n}\n.c-uhff-nav {\n margin: 0 auto;\n max-width: calc(1600px + 10%);\n padding: 0 5%;\n box-sizing: inherit;\n &:before,\n &:after {\n content: ' ';\n display: table;\n clear: left;\n }\n @media only screen and (max-width: 1083px) {\n padding-left: 12px;\n }\n .c-heading-4 {\n color: #616161;\n word-break: break-word;\n font-size: 15px;\n line-height: 20px;\n padding: 36px 0 4px;\n font-weight: 600;\n }\n .c-uhff-nav-row {\n .c-uhff-nav-group {\n display: block;\n float: left;\n min-height: 1px;\n vertical-align: text-top;\n padding: 0 12px;\n width: 100%;\n zoom: 1;\n &:first-child {\n padding-left: 0;\n @media only screen and (max-width: 1083px) {\n padding-left: 12px;\n }\n }\n @media only screen and (min-width: 540px) and (max-width: 1082px) {\n width: 33.33333%;\n }\n @media only screen and (min-width: 1083px) {\n width: 16.6666666667%;\n }\n ul.c-list.f-bare {\n font-size: 11px;\n line-height: 16px;\n margin-top: 0;\n margin-bottom: 0;\n padding-left: 0;\n list-style-type: none;\n li {\n word-break: break-word;\n padding: 8px 0;\n margin: 0;\n }\n }\n }\n }\n}\n.c-uhff-base {\n background: #f2f2f2;\n margin: 0 auto;\n max-width: calc(1600px + 10%);\n padding: 30px 5% 16px;\n &:before,\n &:after {\n content: ' ';\n display: table;\n }\n &:after {\n clear: both;\n }\n a.c-uhff-ccpa {\n font-size: 11px;\n line-height: 16px;\n float: left;\n margin: 3px 0;\n }\n a.c-uhff-ccpa:hover {\n text-decoration: underline;\n }\n ul.c-list {\n font-size: 11px;\n line-height: 16px;\n float: right;\n margin: 3px 0;\n color: #616161;\n li {\n padding: 0 24px 4px 0;\n display: inline-block;\n }\n }\n .c-list.f-bare {\n padding-left: 0;\n list-style-type: none;\n }\n @media only screen and (max-width: 1083px) {\n display: flex;\n flex-wrap: wrap;\n padding: 30px 24px 16px;\n }\n}\n\n.social-share {\n position: fixed;\n top: 60%;\n transform: translateY(-50%);\n left: 0;\n z-index: 1000;\n}\n\n.sharing-options {\n list-style: none;\n padding: 0;\n margin: 0;\n display: block;\n flex-direction: column;\n background-color: white;\n width: 43px;\n border-radius: 0px 7px 7px 0px;\n}\n.linkedin-icon {\n border-top-right-radius: 7px;\n}\n.linkedin-icon:hover {\n border-radius: 0;\n}\n.social-share-rss-image {\n border-bottom-right-radius: 7px;\n}\n.social-share-rss-image:hover {\n border-radius: 0;\n}\n\n.social-link-footer {\n position: relative;\n display: block;\n margin: -2px 0;\n transition: all 0.2s ease;\n}\n.social-link-footer:hover .linkedin-icon {\n border-radius: 0;\n}\n.social-link-footer:hover .social-share-rss-image {\n border-radius: 0;\n}\n\n.social-link-footer img {\n width: 40px;\n height: auto;\n transition: filter 0.3s ease;\n}\n\n.social-share-list {\n width: 40px;\n}\n.social-share-rss-image {\n width: 40px;\n}\n\n.share-icon {\n border: 2px solid transparent;\n display: inline-block;\n position: relative;\n}\n\n.share-icon:hover {\n opacity: 1;\n border: 2px solid white;\n box-sizing: border-box;\n}\n\n.share-icon:hover .label {\n opacity: 1;\n visibility: visible;\n border: 2px solid white;\n box-sizing: border-box;\n border-left: none;\n}\n\n.label {\n position: absolute;\n left: 100%;\n white-space: nowrap;\n opacity: 0;\n visibility: hidden;\n transition: all 0.2s ease;\n color: white;\n border-radius: 0 10 0 10px;\n top: 50%;\n transform: translateY(-50%);\n height: 40px;\n border-radius: 0 6px 6px 0;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 20px 5px 20px 8px;\n margin-left: -1px;\n}\n.linkedin {\n background-color: #0474b4;\n}\n.facebook {\n background-color: #3c5c9c;\n}\n.twitter {\n background-color: white;\n color: black;\n}\n.reddit {\n background-color: #fc4404;\n}\n.mail {\n background-color: #848484;\n}\n.bluesky {\n background-color: white;\n color: black;\n}\n.rss {\n background-color: #ec7b1c;\n}\n#RSS {\n width: 40px;\n height: 40px;\n}\n\n@media (max-width: 991px) {\n .social-share {\n display: none;\n }\n}\n","texts":{"New tab":"What's New","New 1":"Surface Laptop Studio 2","New 2":"Surface Laptop Go 3","New 3":"Surface Pro 9","New 4":"Surface Laptop 5","New 5":"Surface Studio 2+","New 6":"Copilot in Windows","New 7":"Microsoft 365","New 8":"Windows 11 apps","Store tab":"Microsoft Store","Store 1":"Account Profile","Store 2":"Download Center","Store 3":"Microsoft Store Support","Store 4":"Returns","Store 5":"Order tracking","Store 6":"Certified Refurbished","Store 7":"Microsoft Store Promise","Store 8":"Flexible Payments","Education tab":"Education","Edu 1":"Microsoft in education","Edu 2":"Devices for education","Edu 3":"Microsoft Teams for Education","Edu 4":"Microsoft 365 Education","Edu 5":"How to buy for your school","Edu 6":"Educator Training and development","Edu 7":"Deals for students and parents","Edu 8":"Azure for students","Business tab":"Business","Bus 1":"Microsoft Cloud","Bus 2":"Microsoft Security","Bus 3":"Dynamics 365","Bus 4":"Microsoft 365","Bus 5":"Microsoft Power Platform","Bus 6":"Microsoft Teams","Bus 7":"Microsoft Industry","Bus 8":"Small Business","Developer tab":"Developer & IT","Dev 1":"Azure","Dev 2":"Developer Center","Dev 3":"Documentation","Dev 4":"Microsoft Learn","Dev 5":"Microsoft Tech Community","Dev 6":"Azure Marketplace","Dev 7":"AppSource","Dev 8":"Visual Studio","Company tab":"Company","Com 1":"Careers","Com 2":"About Microsoft","Com 3":"Company News","Com 4":"Privacy at Microsoft","Com 5":"Investors","Com 6":"Diversity and inclusion","Com 7":"Accessiblity","Com 8":"Sustainibility"},"defaults":{"config":{"applicablePages":[],"description":"The Microsoft Footer","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"components":[{"id":"custom.widget.MicrosoftFooter","form":null,"config":null,"props":[],"__typename":"Component"}],"grouping":"CUSTOM","__typename":"ComponentTemplate"},"properties":{"config":{"applicablePages":[],"description":"The Microsoft Footer","fetchedContent":null,"__typename":"ComponentConfiguration"},"props":[],"__typename":"ComponentProperties"},"form":null,"__typename":"Component","localOverride":false},"globalCss":{"css":".custom_widget_MicrosoftFooter_context-uhf_105bp_1 {\n min-width: 17.5rem;\n font-size: 0.9375rem;\n box-sizing: border-box;\n -ms-text-size-adjust: 100%;\n -webkit-text-size-adjust: 100%;\n & *,\n & *:before,\n & *:after {\n box-sizing: inherit;\n }\n a.custom_widget_MicrosoftFooter_c-uhff-link_105bp_12 {\n color: #616161;\n word-break: break-word;\n text-decoration: none;\n }\n &a:link,\n &a:focus,\n &a:hover,\n &a:active,\n &a:visited {\n text-decoration: none;\n color: inherit;\n }\n & div {\n font-family: 'Segoe UI', SegoeUI, 'Helvetica Neue', Helvetica, Arial, sans-serif;\n }\n}\n.custom_widget_MicrosoftFooter_c-uhff_105bp_12 {\n background: #f2f2f2;\n margin: -1.5625;\n width: auto;\n height: auto;\n}\n.custom_widget_MicrosoftFooter_c-uhff-nav_105bp_35 {\n margin: 0 auto;\n max-width: calc(100rem + 10%);\n padding: 0 5%;\n box-sizing: inherit;\n &:before,\n &:after {\n content: ' ';\n display: table;\n clear: left;\n }\n @media only screen and (max-width: 1083px) {\n padding-left: 0.75rem;\n }\n .custom_widget_MicrosoftFooter_c-heading-4_105bp_49 {\n color: #616161;\n word-break: break-word;\n font-size: 0.9375rem;\n line-height: 1.25rem;\n padding: 2.25rem 0 0.25rem;\n font-weight: 600;\n }\n .custom_widget_MicrosoftFooter_c-uhff-nav-row_105bp_57 {\n .custom_widget_MicrosoftFooter_c-uhff-nav-group_105bp_58 {\n display: block;\n float: left;\n min-height: 0.0625rem;\n vertical-align: text-top;\n padding: 0 0.75rem;\n width: 100%;\n zoom: 1;\n &:first-child {\n padding-left: 0;\n @media only screen and (max-width: 1083px) {\n padding-left: 0.75rem;\n }\n }\n @media only screen and (min-width: 540px) and (max-width: 1082px) {\n width: 33.33333%;\n }\n @media only screen and (min-width: 1083px) {\n width: 16.6666666667%;\n }\n ul.custom_widget_MicrosoftFooter_c-list_105bp_78.custom_widget_MicrosoftFooter_f-bare_105bp_78 {\n font-size: 0.6875rem;\n line-height: 1rem;\n margin-top: 0;\n margin-bottom: 0;\n padding-left: 0;\n list-style-type: none;\n li {\n word-break: break-word;\n padding: 0.5rem 0;\n margin: 0;\n }\n }\n }\n }\n}\n.custom_widget_MicrosoftFooter_c-uhff-base_105bp_94 {\n background: #f2f2f2;\n margin: 0 auto;\n max-width: calc(100rem + 10%);\n padding: 1.875rem 5% 1rem;\n &:before,\n &:after {\n content: ' ';\n display: table;\n }\n &:after {\n clear: both;\n }\n a.custom_widget_MicrosoftFooter_c-uhff-ccpa_105bp_107 {\n font-size: 0.6875rem;\n line-height: 1rem;\n float: left;\n margin: 0.1875rem 0;\n }\n a.custom_widget_MicrosoftFooter_c-uhff-ccpa_105bp_107:hover {\n text-decoration: underline;\n }\n ul.custom_widget_MicrosoftFooter_c-list_105bp_78 {\n font-size: 0.6875rem;\n line-height: 1rem;\n float: right;\n margin: 0.1875rem 0;\n color: #616161;\n li {\n padding: 0 1.5rem 0.25rem 0;\n display: inline-block;\n }\n }\n .custom_widget_MicrosoftFooter_c-list_105bp_78.custom_widget_MicrosoftFooter_f-bare_105bp_78 {\n padding-left: 0;\n list-style-type: none;\n }\n @media only screen and (max-width: 1083px) {\n display: flex;\n flex-wrap: wrap;\n padding: 1.875rem 1.5rem 1rem;\n }\n}\n.custom_widget_MicrosoftFooter_social-share_105bp_138 {\n position: fixed;\n top: 60%;\n transform: translateY(-50%);\n left: 0;\n z-index: 1000;\n}\n.custom_widget_MicrosoftFooter_sharing-options_105bp_146 {\n list-style: none;\n padding: 0;\n margin: 0;\n display: block;\n flex-direction: column;\n background-color: white;\n width: 2.6875rem;\n border-radius: 0 0.4375rem 0.4375rem 0;\n}\n.custom_widget_MicrosoftFooter_linkedin-icon_105bp_156 {\n border-top-right-radius: 7px;\n}\n.custom_widget_MicrosoftFooter_linkedin-icon_105bp_156:hover {\n border-radius: 0;\n}\n.custom_widget_MicrosoftFooter_social-share-rss-image_105bp_162 {\n border-bottom-right-radius: 7px;\n}\n.custom_widget_MicrosoftFooter_social-share-rss-image_105bp_162:hover {\n border-radius: 0;\n}\n.custom_widget_MicrosoftFooter_social-link-footer_105bp_169 {\n position: relative;\n display: block;\n margin: -0.125rem 0;\n transition: all 0.2s ease;\n}\n.custom_widget_MicrosoftFooter_social-link-footer_105bp_169:hover .custom_widget_MicrosoftFooter_linkedin-icon_105bp_156 {\n border-radius: 0;\n}\n.custom_widget_MicrosoftFooter_social-link-footer_105bp_169:hover .custom_widget_MicrosoftFooter_social-share-rss-image_105bp_162 {\n border-radius: 0;\n}\n.custom_widget_MicrosoftFooter_social-link-footer_105bp_169 img {\n width: 2.5rem;\n height: auto;\n transition: filter 0.3s ease;\n}\n.custom_widget_MicrosoftFooter_social-share-list_105bp_188 {\n width: 2.5rem;\n}\n.custom_widget_MicrosoftFooter_social-share-rss-image_105bp_162 {\n width: 2.5rem;\n}\n.custom_widget_MicrosoftFooter_share-icon_105bp_195 {\n border: 2px solid transparent;\n display: inline-block;\n position: relative;\n}\n.custom_widget_MicrosoftFooter_share-icon_105bp_195:hover {\n opacity: 1;\n border: 2px solid white;\n box-sizing: border-box;\n}\n.custom_widget_MicrosoftFooter_share-icon_105bp_195:hover .custom_widget_MicrosoftFooter_label_105bp_207 {\n opacity: 1;\n visibility: visible;\n border: 2px solid white;\n box-sizing: border-box;\n border-left: none;\n}\n.custom_widget_MicrosoftFooter_label_105bp_207 {\n position: absolute;\n left: 100%;\n white-space: nowrap;\n opacity: 0;\n visibility: hidden;\n transition: all 0.2s ease;\n color: white;\n border-radius: 0 10 0 0.625rem;\n top: 50%;\n transform: translateY(-50%);\n height: 2.5rem;\n border-radius: 0 0.375rem 0.375rem 0;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 1.25rem 0.3125rem 1.25rem 0.5rem;\n margin-left: -0.0625rem;\n}\n.custom_widget_MicrosoftFooter_linkedin_105bp_156 {\n background-color: #0474b4;\n}\n.custom_widget_MicrosoftFooter_facebook_105bp_237 {\n background-color: #3c5c9c;\n}\n.custom_widget_MicrosoftFooter_twitter_105bp_240 {\n background-color: white;\n color: black;\n}\n.custom_widget_MicrosoftFooter_reddit_105bp_244 {\n background-color: #fc4404;\n}\n.custom_widget_MicrosoftFooter_mail_105bp_247 {\n background-color: #848484;\n}\n.custom_widget_MicrosoftFooter_bluesky_105bp_250 {\n background-color: white;\n color: black;\n}\n.custom_widget_MicrosoftFooter_rss_105bp_254 {\n background-color: #ec7b1c;\n}\n#custom_widget_MicrosoftFooter_RSS_105bp_1 {\n width: 2.5rem;\n height: 2.5rem;\n}\n@media (max-width: 991px) {\n .custom_widget_MicrosoftFooter_social-share_105bp_138 {\n display: none;\n }\n}\n","tokens":{"context-uhf":"custom_widget_MicrosoftFooter_context-uhf_105bp_1","c-uhff-link":"custom_widget_MicrosoftFooter_c-uhff-link_105bp_12","c-uhff":"custom_widget_MicrosoftFooter_c-uhff_105bp_12","c-uhff-nav":"custom_widget_MicrosoftFooter_c-uhff-nav_105bp_35","c-heading-4":"custom_widget_MicrosoftFooter_c-heading-4_105bp_49","c-uhff-nav-row":"custom_widget_MicrosoftFooter_c-uhff-nav-row_105bp_57","c-uhff-nav-group":"custom_widget_MicrosoftFooter_c-uhff-nav-group_105bp_58","c-list":"custom_widget_MicrosoftFooter_c-list_105bp_78","f-bare":"custom_widget_MicrosoftFooter_f-bare_105bp_78","c-uhff-base":"custom_widget_MicrosoftFooter_c-uhff-base_105bp_94","c-uhff-ccpa":"custom_widget_MicrosoftFooter_c-uhff-ccpa_105bp_107","social-share":"custom_widget_MicrosoftFooter_social-share_105bp_138","sharing-options":"custom_widget_MicrosoftFooter_sharing-options_105bp_146","linkedin-icon":"custom_widget_MicrosoftFooter_linkedin-icon_105bp_156","social-share-rss-image":"custom_widget_MicrosoftFooter_social-share-rss-image_105bp_162","social-link-footer":"custom_widget_MicrosoftFooter_social-link-footer_105bp_169","social-share-list":"custom_widget_MicrosoftFooter_social-share-list_105bp_188","share-icon":"custom_widget_MicrosoftFooter_share-icon_105bp_195","label":"custom_widget_MicrosoftFooter_label_105bp_207","linkedin":"custom_widget_MicrosoftFooter_linkedin_105bp_156","facebook":"custom_widget_MicrosoftFooter_facebook_105bp_237","twitter":"custom_widget_MicrosoftFooter_twitter_105bp_240","reddit":"custom_widget_MicrosoftFooter_reddit_105bp_244","mail":"custom_widget_MicrosoftFooter_mail_105bp_247","bluesky":"custom_widget_MicrosoftFooter_bluesky_105bp_250","rss":"custom_widget_MicrosoftFooter_rss_105bp_254","RSS":"custom_widget_MicrosoftFooter_RSS_105bp_1"}},"form":null},"localOverride":false},"CachedAsset:text:en_US-components/community/Breadcrumb-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/community/Breadcrumb-1745505310960","value":{"navLabel":"Breadcrumbs","dropdown":"Additional parent page navigation"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageBanner-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageBanner-1745505310960","value":{"messageMarkedAsSpam":"This post has been marked as spam","messageMarkedAsSpam@board:TKB":"This article has been marked as spam","messageMarkedAsSpam@board:BLOG":"This post has been marked as spam","messageMarkedAsSpam@board:FORUM":"This discussion has been marked as spam","messageMarkedAsSpam@board:OCCASION":"This event has been marked as spam","messageMarkedAsSpam@board:IDEA":"This idea has been marked as spam","manageSpam":"Manage Spam","messageMarkedAsAbuse":"This post has been marked as abuse","messageMarkedAsAbuse@board:TKB":"This article has been marked as abuse","messageMarkedAsAbuse@board:BLOG":"This post has been marked as abuse","messageMarkedAsAbuse@board:FORUM":"This discussion has been marked as abuse","messageMarkedAsAbuse@board:OCCASION":"This event has been marked as abuse","messageMarkedAsAbuse@board:IDEA":"This idea has been marked as abuse","preModCommentAuthorText":"This comment will be published as soon as it is approved","preModCommentModeratorText":"This comment is awaiting moderation","messageMarkedAsOther":"This post has been rejected due to other reasons","messageMarkedAsOther@board:TKB":"This article has been rejected due to other reasons","messageMarkedAsOther@board:BLOG":"This post has been rejected due to other reasons","messageMarkedAsOther@board:FORUM":"This discussion has been rejected due to other reasons","messageMarkedAsOther@board:OCCASION":"This event has been rejected due to other reasons","messageMarkedAsOther@board:IDEA":"This idea has been rejected due to other reasons","messageArchived":"This post was archived on {date}","relatedUrl":"View Related Content","relatedContentText":"Showing related content","archivedContentLink":"View Archived Content"},"localOverride":false},"Category:category:Exchange":{"__typename":"Category","id":"category:Exchange","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:Outlook":{"__typename":"Category","id":"category:Outlook","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:Community-Info-Center":{"__typename":"Category","id":"category:Community-Info-Center","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:EducationSector":{"__typename":"Category","id":"category:EducationSector","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:DrivingAdoption":{"__typename":"Category","id":"category:DrivingAdoption","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:Azure":{"__typename":"Category","id":"category:Azure","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:Windows-Server":{"__typename":"Category","id":"category:Windows-Server","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:MicrosoftTeams":{"__typename":"Category","id":"category:MicrosoftTeams","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:PublicSector":{"__typename":"Category","id":"category:PublicSector","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:IoT":{"__typename":"Category","id":"category:IoT","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:HealthcareAndLifeSciences":{"__typename":"Category","id":"category:HealthcareAndLifeSciences","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:ITOpsTalk":{"__typename":"Category","id":"category:ITOpsTalk","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:MicrosoftLearn":{"__typename":"Category","id":"category:MicrosoftLearn","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Blog:board:MicrosoftLearnBlog":{"__typename":"Blog","id":"board:MicrosoftLearnBlog","blogPolicies":{"__typename":"BlogPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}},"boardPolicies":{"__typename":"BoardPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:AI":{"__typename":"Category","id":"category:AI","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:MicrosoftMechanics":{"__typename":"Category","id":"category:MicrosoftMechanics","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:MicrosoftforNonprofits":{"__typename":"Category","id":"category:MicrosoftforNonprofits","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:StartupsatMicrosoft":{"__typename":"Category","id":"category:StartupsatMicrosoft","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:PartnerCommunity":{"__typename":"Category","id":"category:PartnerCommunity","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:Microsoft365Copilot":{"__typename":"Category","id":"category:Microsoft365Copilot","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:Windows":{"__typename":"Category","id":"category:Windows","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:Content_Management":{"__typename":"Category","id":"category:Content_Management","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:microsoft-security":{"__typename":"Category","id":"category:microsoft-security","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"Category:category:microsoftintune":{"__typename":"Category","id":"category:microsoftintune","categoryPolicies":{"__typename":"CategoryPolicies","canReadNode":{"__typename":"PolicyResult","failureReason":null}}},"QueryVariables:TopicReplyList:message:1067893:4":{"__typename":"QueryVariables","id":"TopicReplyList:message:1067893:4","value":{"id":"message:1067893","first":10,"sorts":{"postTime":{"direction":"DESC"}},"repliesFirst":3,"repliesFirstDepthThree":1,"repliesSorts":{"postTime":{"direction":"DESC"}},"useAvatar":true,"useAuthorLogin":true,"useAuthorRank":true,"useBody":true,"useKudosCount":true,"useTimeToRead":false,"useMedia":false,"useReadOnlyIcon":false,"useRepliesCount":true,"useSearchSnippet":false,"useAcceptedSolutionButton":false,"useSolvedBadge":false,"useAttachments":false,"attachmentsFirst":5,"useTags":true,"useNodeAncestors":false,"useUserHoverCard":false,"useNodeHoverCard":false,"useModerationStatus":true,"usePreviewSubjectModal":false,"useMessageStatus":true}},"ROOT_MUTATION":{"__typename":"Mutation"},"CachedAsset:text:en_US-components/community/Navbar-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/community/Navbar-1745505310960","value":{"community":"Community Home","inbox":"Inbox","manageContent":"Manage Content","tos":"Terms of Service","forgotPassword":"Forgot Password","themeEditor":"Theme Editor","edit":"Edit Navigation Bar","skipContent":"Skip to content","gxcuf89792":"Tech Community","external-1":"Events","s-m-b":"Nonprofit Community","windows-server":"Windows Server","education-sector":"Education Sector","driving-adoption":"Driving Adoption","Common-content_management-link":"Content Management","microsoft-learn":"Microsoft Learn","s-q-l-server":"Content Management","partner-community":"Microsoft Partner Community","microsoft365":"Microsoft 365","external-9":".NET","external-8":"Teams","external-7":"Github","products-services":"Products","external-6":"Power Platform","communities-1":"Topics","external-5":"Microsoft Security","planner":"Outlook","external-4":"Microsoft 365","external-3":"Dynamics 365","azure":"Azure","healthcare-and-life-sciences":"Healthcare and Life Sciences","external-2":"Azure","microsoft-mechanics":"Microsoft Mechanics","microsoft-learn-1":"Community","external-10":"Learning Room Directory","microsoft-learn-blog":"Blog","windows":"Windows","i-t-ops-talk":"ITOps Talk","external-link-1":"View All","microsoft-securityand-compliance":"Microsoft Security","public-sector":"Public Sector","community-info-center":"Lounge","external-link-2":"View All","microsoft-teams":"Microsoft Teams","external":"Blogs","microsoft-endpoint-manager":"Microsoft Intune","startupsat-microsoft":"Startups at Microsoft","exchange":"Exchange","a-i":"AI and Machine Learning","io-t":"Internet of Things (IoT)","Common-microsoft365-copilot-link":"Microsoft 365 Copilot","outlook":"Microsoft 365 Copilot","external-link":"Community Hubs","communities":"Products"},"localOverride":false},"CachedAsset:text:en_US-components/community/NavbarHamburgerDropdown-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/community/NavbarHamburgerDropdown-1745505310960","value":{"hamburgerLabel":"Side Menu"},"localOverride":false},"CachedAsset:text:en_US-components/community/BrandLogo-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/community/BrandLogo-1745505310960","value":{"logoAlt":"Khoros","themeLogoAlt":"Brand Logo"},"localOverride":false},"CachedAsset:text:en_US-components/community/NavbarTextLinks-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/community/NavbarTextLinks-1745505310960","value":{"more":"More"},"localOverride":false},"CachedAsset:text:en_US-components/authentication/AuthenticationLink-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/authentication/AuthenticationLink-1745505310960","value":{"title.login":"Sign In","title.registration":"Register","title.forgotPassword":"Forgot Password","title.multiAuthLogin":"Sign In"},"localOverride":false},"CachedAsset:text:en_US-components/nodes/NodeLink-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/nodes/NodeLink-1745505310960","value":{"place":"Place {name}"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageView/MessageViewStandard-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageView/MessageViewStandard-1745505310960","value":{"anonymous":"Anonymous","author":"{messageAuthorLogin}","authorBy":"{messageAuthorLogin}","board":"{messageBoardTitle}","replyToUser":" to {parentAuthor}","showMoreReplies":"Show More","replyText":"Reply","repliesText":"Replies","markedAsSolved":"Marked as Solved","movedMessagePlaceholder.BLOG":"{count, plural, =0 {This comment has been} other {These comments have been} }","movedMessagePlaceholder.TKB":"{count, plural, =0 {This comment has been} other {These comments have been} }","movedMessagePlaceholder.FORUM":"{count, plural, =0 {This reply has been} other {These replies have been} }","movedMessagePlaceholder.IDEA":"{count, plural, =0 {This comment has been} other {These comments have been} }","movedMessagePlaceholder.OCCASION":"{count, plural, =0 {This comment has been} other {These comments have been} }","movedMessagePlaceholderUrlText":"moved.","messageStatus":"Status: ","statusChanged":"Status changed: {previousStatus} to {currentStatus}","statusAdded":"Status added: {status}","statusRemoved":"Status removed: {status}","labelExpand":"expand replies","labelCollapse":"collapse replies","unhelpfulReason.reason1":"Content is outdated","unhelpfulReason.reason2":"Article is missing information","unhelpfulReason.reason3":"Content is for a different Product","unhelpfulReason.reason4":"Doesn't match what I was searching for"},"localOverride":false},"CachedAsset:text:en_US-components/messages/ThreadedReplyList-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/messages/ThreadedReplyList-1745505310960","value":{"title":"{count, plural, one{# Reply} other{# Replies}}","title@board:BLOG":"{count, plural, one{# Comment} other{# Comments}}","title@board:TKB":"{count, plural, one{# Comment} other{# Comments}}","title@board:IDEA":"{count, plural, one{# Comment} other{# Comments}}","title@board:OCCASION":"{count, plural, one{# Comment} other{# Comments}}","noRepliesTitle":"No Replies","noRepliesTitle@board:BLOG":"No Comments","noRepliesTitle@board:TKB":"No Comments","noRepliesTitle@board:IDEA":"No Comments","noRepliesTitle@board:OCCASION":"No Comments","noRepliesDescription":"Be the first to reply","noRepliesDescription@board:BLOG":"Be the first to comment","noRepliesDescription@board:TKB":"Be the first to comment","noRepliesDescription@board:IDEA":"Be the first to comment","noRepliesDescription@board:OCCASION":"Be the first to comment","messageReadOnlyAlert:BLOG":"Comments have been turned off for this post","messageReadOnlyAlert:TKB":"Comments have been turned off for this article","messageReadOnlyAlert:IDEA":"Comments have been turned off for this idea","messageReadOnlyAlert:FORUM":"Replies have been turned off for this discussion","messageReadOnlyAlert:OCCASION":"Comments have been turned off for this event"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageReplyCallToAction-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageReplyCallToAction-1745505310960","value":{"leaveReply":"Leave a reply...","leaveReply@board:BLOG@message:root":"Leave a comment...","leaveReply@board:TKB@message:root":"Leave a comment...","leaveReply@board:IDEA@message:root":"Leave a comment...","leaveReply@board:OCCASION@message:root":"Leave a comment...","repliesTurnedOff.FORUM":"Replies are turned off for this topic","repliesTurnedOff.BLOG":"Comments are turned off for this topic","repliesTurnedOff.TKB":"Comments are turned off for this topic","repliesTurnedOff.IDEA":"Comments are turned off for this topic","repliesTurnedOff.OCCASION":"Comments are turned off for this topic","infoText":"Stop poking me!"},"localOverride":false},"ModerationData:moderation_data:3659195":{"__typename":"ModerationData","id":"moderation_data:3659195","status":"APPROVED","rejectReason":null,"isReportedAbuse":false,"rejectUser":null,"rejectTime":null,"rejectActorType":null},"BlogReplyMessage:message:3659195":{"__typename":"BlogReplyMessage","author":{"__ref":"User:user:44595"},"id":"message:3659195","revisionNum":1,"uid":3659195,"depth":1,"hasGivenKudo":false,"subscribed":false,"board":{"__ref":"Blog:board:ModernWorkAppConsult"},"parent":{"__ref":"BlogTopicMessage:message:1067893"},"conversation":{"__ref":"Conversation:conversation:1067893"},"subject":"Re: Building a React Native module for Windows","moderationData":{"__ref":"ModerationData:moderation_data:3659195"},"body":"
","body@stripHtml({\"removeProcessingText\":false,\"removeSpoilerMarkup\":false,\"removeTocMarkup\":false,\"truncateLength\":200})@stringLength":"213","kudosSumWeight":0,"repliesCount":0,"postTime":"2022-10-22T06:20:52.458-07:00","lastPublishTime":"2022-10-22T06:20:52.458-07:00","metrics":{"__typename":"MessageMetrics","views":1806},"visibilityScope":"PUBLIC","placeholder":false,"originalMessageForPlaceholder":null,"entityType":"BLOG_REPLY","eventPath":"category:microsoft365/category:products-services/category:communities/community:gxcuf89792board:ModernWorkAppConsult/message:1067893/message:3659195","replies":{"__typename":"MessageConnection","pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null},"edges":[]},"customFields":[],"attachments":{"__typename":"AttachmentConnection","edges":[],"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}}},"Rank:rank:37":{"__typename":"Rank","id":"rank:37","position":18,"name":"Copper Contributor","color":"333333","icon":null,"rankStyle":"TEXT"},"User:user:1572676":{"__typename":"User","id":"user:1572676","uid":1572676,"login":"BognaIrena","biography":null,"registrationData":{"__typename":"RegistrationData","status":null,"registrationTime":"2022-10-18T07:31:05.575-07:00"},"deleted":false,"email":"","avatar":{"__typename":"UserAvatar","url":"https://techcommunity.microsoft.com/t5/s/gxcuf89792/m_assets/avatars/default/avatar-8.svg?time=0"},"rank":{"__ref":"Rank:rank:37"},"entityType":"USER","eventPath":"community:gxcuf89792/user:1572676"},"ModerationData:moderation_data:3655756":{"__typename":"ModerationData","id":"moderation_data:3655756","status":"APPROVED","rejectReason":null,"isReportedAbuse":false,"rejectUser":null,"rejectTime":null,"rejectActorType":null},"BlogReplyMessage:message:3655756":{"__typename":"BlogReplyMessage","author":{"__ref":"User:user:1572676"},"id":"message:3655756","revisionNum":1,"uid":3655756,"depth":1,"hasGivenKudo":false,"subscribed":false,"board":{"__ref":"Blog:board:ModernWorkAppConsult"},"parent":{"__ref":"BlogTopicMessage:message:1067893"},"conversation":{"__ref":"Conversation:conversation:1067893"},"subject":"Re: Building a React Native module for Windows","moderationData":{"__ref":"ModerationData:moderation_data:3655756"},"body":"
Hi Matteo Pagani, thank you for the clear and detailed instruction! I have encounter one problem that I was not able to figure out: when adding references to my C# project, the \"Shared Projects\" section is empty and I was not able to get \"Microsoft.ReactNative.SharedManaged\" from anywhere. In a C++ project in the same solution the \"Microsoft.ReactNative.Cxx\" reference is available. My project is based on react-native-windows 70.0.3 and I found https://github.com/microsoft/react-native-windows/pull/5091 saying that SharedManaged was removed from the package, but I have no idea how to replace it or where was it moved. Any chance you could point me in the right direction, please?
Best regards,
Bogna
","body@stripHtml({\"removeProcessingText\":false,\"removeSpoilerMarkup\":false,\"removeTocMarkup\":false,\"truncateLength\":200})@stringLength":"208","kudosSumWeight":0,"repliesCount":0,"postTime":"2022-10-18T07:46:16.184-07:00","lastPublishTime":"2022-10-18T07:46:16.184-07:00","metrics":{"__typename":"MessageMetrics","views":1831},"visibilityScope":"PUBLIC","placeholder":false,"originalMessageForPlaceholder":null,"entityType":"BLOG_REPLY","eventPath":"category:microsoft365/category:products-services/category:communities/community:gxcuf89792board:ModernWorkAppConsult/message:1067893/message:3655756","replies":{"__typename":"MessageConnection","pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null},"edges":[]},"customFields":[],"attachments":{"__typename":"AttachmentConnection","edges":[],"pageInfo":{"__typename":"PageInfo","hasNextPage":false,"endCursor":null,"hasPreviousPage":false,"startCursor":null}}},"CachedAsset:text:en_US-components/community/NavbarDropdownToggle-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/community/NavbarDropdownToggle-1745505310960","value":{"ariaLabelClosed":"Press the down arrow to open the menu"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/common/QueryHandler-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/common/QueryHandler-1745505310960","value":{"title":"Query Handler"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageCoverImage-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageCoverImage-1745505310960","value":{"coverImageTitle":"Cover Image"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/nodes/NodeTitle-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/nodes/NodeTitle-1745505310960","value":{"nodeTitle":"{nodeTitle, select, community {Community} other {{nodeTitle}}} "},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageTimeToRead-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageTimeToRead-1745505310960","value":{"minReadText":"{min} MIN READ"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageSubject-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageSubject-1745505310960","value":{"noSubject":"(no subject)"},"localOverride":false},"CachedAsset:text:en_US-components/users/UserLink-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/users/UserLink-1745505310960","value":{"authorName":"View Profile: {author}","anonymous":"Anonymous"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/users/UserRank-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/users/UserRank-1745505310960","value":{"rankName":"{rankName}","userRank":"Author rank {rankName}"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageTime-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageTime-1745505310960","value":{"postTime":"Published: {time}","lastPublishTime":"Last Update: {time}","conversation.lastPostingActivityTime":"Last posting activity time: {time}","conversation.lastPostTime":"Last post time: {time}","moderationData.rejectTime":"Rejected time: {time}"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageBody-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageBody-1745505310960","value":{"showMessageBody":"Show More","mentionsErrorTitle":"{mentionsType, select, board {Board} user {User} message {Message} other {}} No Longer Available","mentionsErrorMessage":"The {mentionsType} you are trying to view has been removed from the community.","videoProcessing":"Video is being processed. Please try again in a few minutes.","bannerTitle":"Video provider requires cookies to play the video. Accept to continue or {url} it directly on the provider's site.","buttonTitle":"Accept","urlText":"watch"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageCustomFields-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageCustomFields-1745505310960","value":{"CustomField.default.label":"Value of {name}"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageRevision-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageRevision-1745505310960","value":{"lastUpdatedDatePublished":"{publishCount, plural, one{Published} other{Updated}} {date}","lastUpdatedDateDraft":"Created {date}","version":"Version {major}.{minor}"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageReplyButton-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageReplyButton-1745505310960","value":{"repliesCount":"{count}","title":"Reply","title@board:BLOG@message:root":"Comment","title@board:TKB@message:root":"Comment","title@board:IDEA@message:root":"Comment","title@board:OCCASION@message:root":"Comment"},"localOverride":false},"CachedAsset:text:en_US-components/messages/MessageAuthorBio-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/messages/MessageAuthorBio-1745505310960","value":{"sendMessage":"Send Message","actionMessage":"Follow this blog board to get notified when there's new activity","coAuthor":"CO-PUBLISHER","contributor":"CONTRIBUTOR","userProfile":"View Profile","iconlink":"Go to {name} {type}"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/users/UserAvatar-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/users/UserAvatar-1745505310960","value":{"altText":"{login}'s avatar","altTextGeneric":"User's avatar"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/ranks/UserRankLabel-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/ranks/UserRankLabel-1745505310960","value":{"altTitle":"Icon for {rankName} rank"},"localOverride":false},"CachedAsset:text:en_US-components/users/UserRegistrationDate-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-components/users/UserRegistrationDate-1745505310960","value":{"noPrefix":"{date}","withPrefix":"Joined {date}"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/nodes/NodeAvatar-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/nodes/NodeAvatar-1745505310960","value":{"altTitle":"Node avatar for {nodeTitle}"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/nodes/NodeDescription-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/nodes/NodeDescription-1745505310960","value":{"description":"{description}"},"localOverride":false},"CachedAsset:text:en_US-shared/client/components/nodes/NodeIcon-1745505310960":{"__typename":"CachedAsset","id":"text:en_US-shared/client/components/nodes/NodeIcon-1745505310960","value":{"contentType":"Content Type {style, select, FORUM {Forum} BLOG {Blog} TKB {Knowledge Base} IDEA {Ideas} OCCASION {Events} other {}} icon"},"localOverride":false}}}},"page":"/blogs/BlogMessagePage/BlogMessagePage","query":{"boardId":"modernworkappconsult","messageSubject":"building-a-react-native-module-for-windows","messageId":"1067893"},"buildId":"HEhyUrv5OXNBIbfCLaOrw","runtimeConfig":{"buildInformationVisible":false,"logLevelApp":"info","logLevelMetrics":"info","openTelemetryClientEnabled":false,"openTelemetryConfigName":"o365","openTelemetryServiceVersion":"25.1.0","openTelemetryUniverse":"prod","openTelemetryCollector":"http://localhost:4318","openTelemetryRouteChangeAllowedTime":"5000","apolloDevToolsEnabled":false,"inboxMuteWipFeatureEnabled":false},"isFallback":false,"isExperimentalCompile":false,"dynamicIds":["./components/community/Navbar/NavbarWidget.tsx","./components/community/Breadcrumb/BreadcrumbWidget.tsx","./components/customComponent/CustomComponent/CustomComponent.tsx","./components/blogs/BlogArticleWidget/BlogArticleWidget.tsx","./components/external/components/ExternalComponent.tsx","./components/messages/MessageView/MessageViewStandard/MessageViewStandard.tsx","./components/messages/ThreadedReplyList/ThreadedReplyList.tsx","../shared/client/components/common/List/UnstyledList/UnstyledList.tsx","./components/messages/MessageView/MessageView.tsx"],"appGip":true,"scriptLoader":[{"id":"analytics","src":"https://techcommunity.microsoft.com/t5/s/gxcuf89792/pagescripts/1730819800000/analytics.js?page.id=BlogMessagePage&entity.id=board%3Amodernworkappconsult&entity.id=message%3A1067893","strategy":"afterInteractive"}]}