{"id":131,"date":"2021-06-30T12:00:00","date_gmt":"2021-06-30T19:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/react-native\/?p=131"},"modified":"2022-11-18T03:27:14","modified_gmt":"2022-11-18T11:27:14","slug":"react-native-windows-notifications","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/react-native\/react-native-windows-notifications\/","title":{"rendered":"Add Toast notifications to your React Native for Windows application"},"content":{"rendered":"<p>In this article, we will discuss how to enable your app to show toast notifications, which you can use to notify the user that something needs their attention, or to notify them that a long running operation has completed.<\/p>\n<h3>Create a native module<\/h3>\n<p>This article assumes you have some familiarity with React Native for Windows native modules. If you need a refresher, check out <a href=\"https:\/\/microsoft.github.io\/react-native-windows\/docs\/next\/native-modules\">the native modules documentation<\/a>.<\/p>\n<p>The Windows toast notification API is fairly rich: it includes a number of UI <em>templates<\/em> that an app can use to customize how it wants the notification to look.\nSome templates include text only, while others include text and images. Every template has some combination of these. You can see a list of the available templates <a href=\"\/\/docs.microsoft.com\/en-us\/previous-versions\/windows\/apps\/hh761494(v=win.10)&gt;\">here<\/a>.<\/p>\n<p>We will create a native module which will have a single method, <code>raise<\/code>. The Windows toast notification API takes an XML document as input, which will specify the text and image sources that we want to show. However, it would be much more natural for us to work with JavaScript objects and JSON instead of XML markup.\nSo our <code>raise<\/code> method will take a JS object, which will include what template the app wants to use, as well as arrays of text and images that apply to the template. We will take the information in this JS object, and use it to construct the XML that we need to pass to the platform API.<\/p>\n<h3>Create a module<\/h3>\n<p>For the purpose of our demo, we will add a native module directly to a C++\/WinRT example app. If you prefer, you can follow the steps below and apply them to a separate native module project. Note that you can also use C# to write native modules in a similar manner. For more information about choosing between C++\/WinRT and C#, see <a href=\"https:\/\/microsoft.github.io\/react-native-windows\/docs\/native-code-language-choice\">Native code language choice<\/a>.\nWe will start with our standard native module setup, by adding a <code>Notifications.h<\/code> file to our example app project. In this initial version, we&#8217;ll use the simplest static Toast template (one string of text), so we&#8217;ll have our <code>raise<\/code> method take a string:<\/p>\n<pre><code class=\"language-cpp\">#pragma once\n#include &lt;NativeModules.h&gt;\n#include &lt;winrt\/Windows.UI.Notifications.h&gt;\n#include &lt;winrt\/Windows.Data.Xml.Dom.h&gt;\n\nusing namespace winrt::Windows::UI::Notifications;\nusing namespace winrt::Windows::Data::Xml::Dom;\n\nREACT_MODULE(Notifications)\nstruct Notifications {\n\n  REACT_INIT(Initialize);\n  void Initialize(React::ReactContext const&amp; context) noexcept {\n    m_context = context;\n  }\n\n  REACT_METHOD(Raise, L\"raise\");\n  void Raise(const std::string&amp; text) noexcept\n  {\n  }\n\nprivate:\n  React::ReactContext m_context;\n};<\/code><\/pre>\n<p>Then in our app&#8217;s <code>ReactPackageProvider.cpp<\/code>, we just include our new header:<\/p>\n<pre><code class=\"language-diff\">#include \"pch.h\"\n#include \"ReactPackageProvider.h\"\n#include \"NativeModules.h\"\n+#include \"Notifications.h\"\nusing namespace winrt::Microsoft::ReactNative;\n\nnamespace winrt::example::implementation\n{\n\nvoid ReactPackageProvider::CreatePackage(IReactPackageBuilder const &amp;packageBuilder) noexcept\n{\n    AddAttributedModules(packageBuilder);\n}\n\n} \/\/ namespace winrt::example::implementation<\/code><\/pre>\n<h3>Starting simple<\/h3>\n<p>In our JavaScript (<code>App.tsx<\/code> or <code>App.js<\/code>), we will import NativeModules and add a button that calls into our <code>raise<\/code> API when clicked. Here we are using a <a href=\"https:\/\/github.com\/asklar\/react-native-xaml\">react-native-xaml<\/a> Button component, but you can use any UI component and toolkit you&#8217;d like.\nNormally, you would call the <code>raise<\/code> API in response to some other event, like an upload\/download operation completing.<\/p>\n<pre><code class=\"language-jsx\">import {\n  \/\/ ...\n  NativeModules,\n} from \"react-native\";\nimport { Button } from \"react-native-xaml\";\n\n\/\/ ...\n&lt;Button\n  onClick={() =&gt; {\n    NativeModules.Notifications.raise(\"hello\");\n  }}\n  content=\"click me to send a toast\"\n\/&gt;;<\/code><\/pre>\n<p>So now we have the basic setup, and you can set a breakpoint in the C++ code and validate that clicking on the button breaks into the C++ <code>Raise<\/code> method.\nNow we get to have fun. First let&#8217;s try the simplest static Toast template, <code>ToastText01<\/code>. This template just has one value for us to fill out, the text we want to show.\nHere&#8217;s the logic to send a toast using this template, in C++:<\/p>\n<pre><code class=\"language-cpp\">  REACT_METHOD(Raise, L\"raise\");\n  void Raise(const std::string&amp; textToShow) noexcept\n  {\n    auto xml = ToastNotificationManager::GetTemplateContent(ToastTemplateType::ToastText01);\n    for (auto&amp; element : xml.GetElementsByTagName(L\"text\")) {\n      element.AppendChild(xml.CreateTextNode(winrt::to_hstring(textToShow)));\n    }\n\n    auto toast = ToastNotification(xml);\n    ToastNotificationManager::CreateToastNotifier().Show(toast);\n  }<\/code><\/pre>\n<h3>Complete solution<\/h3>\n<p>Note that it is necessary to convert the string values we get from JavaScript (as <code>std::string<\/code> values which are UTF-8) onto platform strings (<code>winrt::hstring<\/code>), which are UTF-16, so we use the <code>winrt::to_hstring<\/code> helper for that.\nNow that we have the simple case, it&#8217;s time to crank it up a notch: we want to be able to use any of the available toast templates, which can have a number of text strings and images. The image elements have a <code>src<\/code> attribute as well as an <code>alt<\/code> attribute for accessibility.<\/p>\n<pre><code class=\"language-cpp\">  REACT_METHOD(Raise, L\"raise\");\n  void Raise(const React::JSValue&amp; notification) noexcept\n  {\n\n    ToastTemplateType type = ToastTemplateType::ToastText01;\n\n    React::JSValueObject obj;\n    if (notification.Type() == React::JSValueType::String)\n    {\n      obj[\"text\"] = notification.AsString();\n    }\n    else {\n      obj = notification.AsObject().Copy();\n    }\n\n    auto typeEntry = obj.find(\"template\");\n    if (typeEntry != obj.end() &amp;&amp; typeEntry-&gt;second.Type() == React::JSValueType::Int64) {\n      type = static_cast&lt;ToastTemplateType&gt;(typeEntry-&gt;second.AsInt32());\n    }\n\n    auto xml = ToastNotificationManager::GetTemplateContent(type);\n\n    for (const auto&amp; entry : obj)\n    {\n      const auto tagName = winrt::to_hstring(entry.first);\n      auto xmlElements = xml.GetElementsByTagName(tagName);\n\n      if (entry.second.Type() == React::JSValueType::String) {\n        React::JSValueArray strToArray;\n        strToArray.push_back(entry.second.AsString());\n        FillXmlElements(xml, xmlElements, strToArray);\n      }\n      else if (entry.second.Type() == React::JSValueType::Object) {\n        React::JSValueArray objToArray;\n        objToArray.push_back(entry.second.AsObject().Copy());\n        FillXmlElements(xml, xmlElements, objToArray);\n      } else {\n        FillXmlElements(xml, xmlElements, entry.second.AsArray());\n      }\n    }\n\n    auto toast = winrt::Windows::UI::Notifications::ToastNotification(xml);\n    ToastNotificationManager::CreateToastNotifier().Show(toast);\n  }\n\nprivate:\n  React::ReactContext m_context;\n\n  void FillXmlElements(const XmlDocument&amp; xml, const XmlNodeList&amp; xmlElements, const React::JSValueArray&amp; arr) {\n    int i = 0;\n    for (const auto&amp; arrValue : arr) {\n      auto node = xmlElements.GetAt(i++);\n      if (arrValue.Type() == React::JSValueType::String) {\n        const auto value = winrt::to_hstring(arrValue.AsString());\n        node.AppendChild(xml.CreateTextNode(value));\n      }\n      else if (arrValue.Type() == React::JSValueType::Object) {\n        const auto&amp; arrValueObj = arrValue.AsObject();\n        for (const auto&amp; entry : arrValueObj) {\n          auto attrName = winrt::to_hstring(entry.first);\n          auto attr = node.Attributes().GetNamedItem(attrName);\n          if (!attr) {\n            attr = xml.CreateAttribute(attrName);\n            node.Attributes().SetNamedItem(attr);\n          }\n\n          attr.NodeValue(winrt::box_value(winrt::to_hstring(entry.second.AsString())));\n        }\n      }\n    }\n  }<\/code><\/pre>\n<p>The code above will enable us to specify a <code>template<\/code> attribute, that we will set to be the numeric value of the template we want to use. You can find a table of such values in the <a href=\"https:\/\/docs.microsoft.com\/uwp\/api\/Windows.UI.Notifications.ToastTemplateType\">ToastTemplateType docs<\/a>. For example, <code>ToastText01<\/code> has a value of 4, so we&#8217;d specify <code>template: 4<\/code>.\nIt will also iterate through the rest of our JavaScript object parameter to discover the key\/value pairs we want to set and apply them to the underlying XML that we will later pass to the platform.<\/p>\n<p>Here is how you&#8217;d use this complete version from JavaScript:<\/p>\n<pre><code class=\"language-jsx\">&lt;Button\n  onClick={() =&gt; {\n    NativeModules.Notifications.raise({\n      template: 0, \/\/ ToastImageAndText01 - see https:\/\/docs.microsoft.com\/uwp\/api\/Windows.UI.Notifications.ToastTemplateType\n      \/\/ The template schema can be found at https:\/\/docs.microsoft.com\/previous-versions\/windows\/apps\/hh761494(v=win.10)\n      text: \"hello world\",\n      image: {\n        src: \"https:\/\/microsoft.github.io\/react-native-windows\/img\/header_logo.svg\",\n        alt: \"React logo\",\n      },\n    });\n  }}\n  content=\"click me to send a toast\"\n\/&gt;<\/code><\/pre>\n<h3>Wrapping up<\/h3>\n<p>In this article we have learned how to write a native module to call into the native Toast Notification APIs, and how to use the JSValue APIs to inspect values sent from the JavaScript side to the native module<\/p>\n<p>You can find the <code>Notifications.h<\/code> header used in this article as a <a href=\"https:\/\/gist.github.com\/asklar\/12273ce7991c084d7d5c357206174b2d\">GitHub Gist<\/a>.<\/p>\n<p>If you\u2019re interested in getting started with React Native for Windows, check out our website at <a href=\"https:\/\/aka.ms\/reactnative\">aka.ms\/reactnative<\/a>.<\/p>\n<p>You can also follow us on Twitter <a href=\"https:\/\/twitter.com\/reactnativemsft\">@ReactNativeMSFT<\/a> to keep up to date on news, feature roadmaps, and more.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Add Windows toast notifications to a React Native for Windows application<\/p>\n","protected":false},"author":23376,"featured_media":156,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[9,2,3,8,6],"class_list":["post-131","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-react-native","tag-notifications","tag-react-native","tag-react-native-windows","tag-tutorial","tag-windows"],"acf":[],"blog_post_summary":"<p>Add Windows toast notifications to a React Native for Windows application<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/react-native\/wp-json\/wp\/v2\/posts\/131","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/react-native\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/react-native\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/react-native\/wp-json\/wp\/v2\/users\/23376"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/react-native\/wp-json\/wp\/v2\/comments?post=131"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/react-native\/wp-json\/wp\/v2\/posts\/131\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/react-native\/wp-json\/wp\/v2\/media\/156"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/react-native\/wp-json\/wp\/v2\/media?parent=131"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/react-native\/wp-json\/wp\/v2\/categories?post=131"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/react-native\/wp-json\/wp\/v2\/tags?post=131"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}