react-native-xaml works best on the latest supported react-native-windows version.
To include react-native-xaml in your app, just add it to your package.json:
yarn add react-native-xaml --save
Then autolinking will take care of the rest!
The stock react-native-xaml package includes projections for all XAML types (i.e. "system XAML") as well as WinUI 2.x.
You must update your app to use WinUI 2.6 or later. See the instructions at https://microsoft.github.io/react-native-windows/docs/customizing-sdk-versions.
If you want to use an existing UserControl
(e.g. from a library you've already written), you will want to generate a custom projection for your project.
For more information on generating a custom projection, please see the Technical Guide.
Below are usage examples of some controls and features of react-native-xaml. If you have questions about a scenario you don't see below, please file an issue!
<StackPanel orientation="horizontal">
<HyperlinkButton content="Click me!" onClick={(args) => {
alert(`clicked! Native event args: ${JSON.stringify(args.nativeEvent)}`);
}} />
<Border verticalAlignment="center" background="paleturquoise" >
<TextBlock text="this is a text block" foreground='red' textAlignment="center" />
</Border>
<TextBlock text="this is another text block" foreground='green' textAlignment="center" />
<Button content="this is a button" onClick={() => { alert("you clicked the button!"); }} />
</StackPanel>
<TextBlock foreground="black" xamlPadding={20} xamlMargin={20}>
<Run text="hello world!"/>
<LineBreak />
<Italic>
<Run text="hi there"/>
</Italic>
<Hyperlink navigateUri="http://bing.com">
<Run text="Go to bing"/>
</Hyperlink>
</TextBlock>
[isOpen, setIsOpen] = useState(false);
<TextBox text="this is a textbox with a menuFlyout" foreground="red">
<MenuFlyout isOpen={isOpen} onClosed={() => {
setIsOpen(false);
}} >
<MenuFlyoutItem text="option 1" onClick={(x) => { alert(JSON.stringify(x.nativeEvent)); setOption("option 1"); }} />
<MenuFlyoutItem text="option 2" onClick={() => { alert("clicked 2"); setOption("option 2"); }}/>
</MenuFlyout>
</TextBox>
<Button content={`Last selected option = ${option} ${count}`}
onClick={(a) => {
setIsOpen(true); }} />
Menus can contain cascading items:
<MenuFlyout ...>
<MenuFlyoutSubItem text='subitem 1'>
<MenuFlyoutItem text='item 1'/>
<MenuFlyoutItem text='item 2'/>
<MenuFlyoutItem text='item 3'/>
</MenuFlyoutSubItem>
<MenuFlyoutSubItem text='subitem 2'>
<MenuFlyoutItem text='item 4'/>
<MenuFlyoutItem text='item 5'/>
<MenuFlyoutItem text='item 6'/>
</MenuFlyoutSubItem>
</MenuFlyout>
<MenuBar>
<MenuBarItem title="File">
<MenuFlyoutItem text="Help"/>
<MenuFlyoutItem text="Settings"/>
</MenuBarItem>
<MenuBarItem title="View"/>
</MenuBar>
- For Image, note that you should specify its size so that RN reserves space for it, otherwise the image won't show.
- Supports Bitmap images and SVG files URIs, as well as inline Base64-encoded data:
<Image source="https://microsoft.github.io/react-native-windows/img/homepage/cross-platform.png"
width={200} height={100} />
<Image
source="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4gPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyOCAyOCIgZmlsbD0ibm9uZSI+PHBhdGggZD0iTTEzLjEyNSAwSDBWMTMuMTI1SDEzLjEyNVYwWiIgZmlsbD0iI0YyNTAyMiI+PC9wYXRoPjxwYXRoIGQ9Ik0yOCAwSDE0Ljg3NVYxMy4xMjVIMjhWMFoiIGZpbGw9IiM3RkJBMDAiPjwvcGF0aD48cGF0aCBkPSJNMTMuMTI1IDE0Ljg3NUgwVjI4SDEzLjEyNVYxNC44NzVaIiBmaWxsPSIjMDBBNEVGIj48L3BhdGg+PHBhdGggZD0iTTI4IDE0Ljg3NUgxNC44NzVWMjhIMjhWMTQuODc1WiIgZmlsbD0iI0ZGQjkwMCI+PC9wYXRoPjwvc3ZnPiA="
width={80}
height={80} />
<ComboBox text="this is a combobox" description="best pets"
onSelectionChanged={(args) =>
{ alert(`sel changed! Native event args: ${JSON.stringify(args.nativeEvent)}`); }
} >
<ComboBoxItem content="garfield" foreground="black" />
<ComboBoxItem content="snoopy" foreground="black" />
</ComboBox>
<AutoSuggestBox placeholderText="placeholder text">
<SymbolIcon symbol={Symbol.Find}/>
</AutoSuggestBox>
<Grid gridLayout={{columns: [60, 60, '*'], rows: [100, 100, 100]}}>
<TextBlock text="hello" gridRow={2} gridColumn={2} />
<TextBlock text="world" gridRow={1} gridColumn={1} />
<TextBlock gridRow={2} gridColumn={1} text='hi there' />
</Grid>
Note that only react-native-xaml components will respect the gridRow
/gridColumn
properties inside a Grid
, core controls will not do that.
const [text, setText] = React.useState('initial text');
<WinUI.NavigationView width={700} height={700}>
<WinUI.NavigationViewItem content='Item 1' onTapped={() => setText('text #1')} priority={NavigationViewPriority.MenuItem}>
<FontIcon glyph="" />
</WinUI.NavigationViewItem>
<WinUI.NavigationViewItem content='Item 2' onTapped={() => setText('text #2')} priority={NavigationViewPriority.FooterMenuItem}/>
<TextBlock text={text} priority={NavigationViewPriority.Content}/>
</WinUI.NavigationView>
<CommandBar isOpen={true} style={{ height: 56 }}>
<AppBarButton
priority={CommandBarPriority.PrimaryCommand}
label="Search"
onClick={() => { alert('search'); }}>
<SymbolIcon symbol={Symbol.Find} />
<KeyboardAccelerator
virtualKey={VirtualKey.S}
modifiers={VirtualKeyModifiers.Menu} />
</AppBarButton>
<AppBarButton
priority={CommandBarPriority.PrimaryCommand}
label="Calendar">
<SymbolIcon symbol={Symbol.Calendar} />
</AppBarButton>
<AppBarButton label="Audio" priority={CommandBarPriority.SecondaryCommand}>
<SymbolIcon symbol={Symbol.Audio} />
</AppBarButton>
<AppBarButton
label="Calculator"
priority={CommandBarPriority.SecondaryCommand}>
<SymbolIcon symbol={Symbol.Calculator} />
</AppBarButton>
</CommandBar>
const [showState, setShowState] = useState(ContentDialogState.Hidden);
<Button
onTapped={a => {setShowState(ContentDialogState.Popup);}}
content="click to open a ContentDialog" />
<ContentDialog
showState={showState}
defaultButton={ContentDialogButton.Close}
title="the title"
content="this is the content"
closeButtonText="close"
primaryButtonText="primary"
secondaryButtonText="secondary"
onPrimaryButtonClick={e => {
alert('primary');
}}
onSecondaryButtonClick={e => {
alert('secondary');
}}
onContentDialogClosed={e => {
setShowState(ContentDialogState.Hidden);
alert(e.nativeEvent);
}} />
const [isOpen, setIsOpen] = useState(false);
// ...
<SplitView
isPaneOpen={isOpen}
onPaneClosed={() => {
setIsOpen(false);
}}
width={800}
height={300}
paneBackground="red"
panePlacement={SplitViewPanePlacement.Left}>
<TextBlock
text="this is in the pane"
priority={SplitViewPriority.Pane}
foreground="white"
/>
<Grid
background="green"
priority={SplitViewPriority.Content}
gridLayout={{rows: [200], columns: [200]}}>
<TextBlock text="this is in the content" foreground="white" />
</Grid>
</SplitView>
<Button
foreground="#992222"
onTapped={a => {
setIsOpen(!isOpen);
}}
content="button"
/>
<Path
data="M14,2H12V1H11V2H5V1H4V2H2V14H14ZM4,3V4H5V3h6V4h1V3h1V5H3V3Zm9,10H3V6H13ZM7,10V7H4v3ZM5,8H6V9H5Z"
width={16}
height={16}
fill="red"
horizontalAlignment={HorizontalAlignment.Center}
verticalAlignment={VerticalAlignment.Center}
/>
Using resources
to style specific aspects of a control
<Button
content="Hello with style!"
resources={{
ButtonForeground: "#00fff1",
ButtonForegroundPressed: "#2090ff",
}}
/>
Alternatively, use styleKey
to leverage system theme styles.
<Button
content="Hello with accent style!"
styleKey="AccentButtonStyle"
/>
Note:
"AccentButtonStyle"
used in this example is defined here. Controls will have their supportedstyleKey
s defined in associated *_themeresources.xaml files like the one linked.
react-native-xaml supports raising events and sending event args to the registered JavaScript event handlers. The native XAML event args object is projected to JavaScript through the nativeEvent
method:
<HyperlinkButton content="Click me!"
onClick={(args) => {
alert(JSON.stringify(args.nativeEvent));
}} />
The content of the native event args object depends on whether the app is using Direct debugging or Web debugging.
-
In web debugging mode, only certain properties on the sender and args are exposed. There is some special casing for a small number of events so that the "important properties" for the event can be projected to JavaScript. These properties must be of simple types (
number
,string
,boolean
, arrays of simple types, or objects with members of simple types). -
In direct debugging mode, react-native-xaml uses codegen to infer how to marshal WinRT objects onto JavaScript by using JSI. This is a much richer and generic functionality. With this approach, you have access to the full set of properties on the event args and sender.
Sample JSON from an event args for a button being tapped
{
"sender": {
"flowDirection": 0,
"className": "Windows.UI.Xaml.Controls.Button",
"allowFocusOnInteraction": true,
"isFocusEngagementEnabled": false,
"focusVisualMargin": {
"left": -3,
"top": -3,
"right": -3,
"bottom": -3
},
"allowFocusWhenDisabled": false,
"background": "Black",
"isTextScaleFactorEnabled": true,
"focusVisualSecondaryBrush": "White",
"backgroundSizing": 1,
"borderBrush": "White",
"horizontalAlignment": 0,
"borderThickness": {
"left": 1,
"top": 1,
"right": 1,
"bottom": 1
},
"characterSpacing": 0,
"clickMode": 0,
"defaultStyleKey": {
"kind": 1,
"name": "Windows.UI.Xaml.Controls.Button"
},
"commandParameter": null,
"content": "button",
"focusVisualPrimaryThickness": {
"left": 2,
"top": 2,
"right": 2,
"bottom": 2
},
"dataContext": null,
"height": 32,
"defaultStyleResourceUri": null,
"elementSoundMode": 0,
"tabIndex": 2147483647,
"focusVisualPrimaryBrush": "Black",
"focusVisualSecondaryThickness": {
"left": 1,
"top": 1,
"right": 1,
"bottom": 1
},
"fontSize": 14,
"margin": {
"left": 0,
"top": 0,
"right": 0,
"bottom": 0
},
"fontStretch": 5,
"isTabStop": true,
"fontStyle": 0,
"foreground": "Dark red",
"horizontalContentAlignment": 1,
"tabNavigation": 0,
"isEnabled": true,
"isFocusEngaged": false,
"language": "en-US",
"maxHeight": null,
"maxWidth": null,
"tag": 13,
"minHeight": 0,
"minWidth": 0,
"name": "<reacttag>: 13",
"padding": {
"left": 8,
"top": 5,
"right": 8,
"bottom": 6
},
"requestedTheme": 0,
"requiresPointer": 0,
"verticalAlignment": 1,
"useSystemFocusVisuals": true,
"verticalContentAlignment": 1,
"width": 1200
},
"args": {
"className": "Windows.UI.Xaml.Input.TappedRoutedEventArgs",
"handled": false,
"pointerDeviceType": 2,
"originalSource": {
"flowDirection": 0,
"className": "Windows.UI.Xaml.Controls.TextBlock",
"allowFocusOnInteraction": true,
"focusVisualMargin": {
"left": 0,
"top": 0,
"right": 0,
"bottom": 0
},
"allowFocusWhenDisabled": false,
"characterSpacing": 0,
"maxLines": 0,
"isTextSelectionEnabled": false,
"focusVisualPrimaryThickness": {
"left": 2,
"top": 2,
"right": 2,
"bottom": 2
},
"dataContext": "button",
"focusVisualPrimaryBrush": "Black",
"isTextScaleFactorEnabled": true,
"focusVisualSecondaryBrush": "White",
"focusVisualSecondaryThickness": {
"left": 1,
"top": 1,
"right": 1,
"bottom": 1
},
"fontSize": 14,
"margin": {
"left": 0,
"top": 0,
"right": 0,
"bottom": 0
},
"fontStretch": 5,
"lineHeight": 0,
"fontStyle": 0,
"foreground": "Black",
"height": null,
"horizontalAlignment": 0,
"horizontalTextAlignment": 1,
"isColorFontEnabled": true,
"language": "en-US",
"lineStackingStrategy": 0,
"maxHeight": null,
"maxWidth": null,
"tag": null,
"minHeight": 0,
"minWidth": 0,
"name": "",
"opticalMarginAlignment": 0,
"padding": {
"left": 0,
"top": 0,
"right": 0,
"bottom": 0
},
"requestedTheme": 0,
"selectionHighlightColor": "Blue",
"text": "button",
"textAlignment": 1,
"textReadingOrder": 1,
"textLineBounds": 0,
"textDecorations": 0,
"textTrimming": 0,
"textWrapping": 1,
"verticalAlignment": 0,
"width": null
}
}
}
In order to enable Direct debugging for your app, make sure that your App.cpp/App.cs has disabled web debugging:
InstanceSettings().UseWebDebugger(false);
InstanceSettings.UseWebDebugger = false;
By default, only XAML properties of event arg objects are exposed back. Some event args support calling methods on them.
See eventArgMethods
in Windows.UI.Xaml.json.
Example:
/// assume there is a MenuFlyout _menuRef defined like this:
const menu = useRef<MenuFlyoutRef>(null);
/// then:
<TextBlock
text="Hello"
onContextRequested={e => {
const tag = findNodeHandle(_tbRef.current);
const { point, returnValue } = e.nativeEvent.args.TryGetPosition(tag);
MenuFlyout.ShowAt(_menuRef, {point: point});
}}
/>
Some types support custom commands to expose some functionality of the underlying platform. For example, this allows calling the programatically showing a flyout menu:
const _tbRef = React.useRef<TextBlockRef>(null);
const _menuRef = useRef<MenuFlyoutRef>(null);
const [x, setX] = React.useState(100);
// ...
return
<TextBox
text={x}
onBeforeTextChanging={e => {
setX(e.nativeEvent.args.newText);
}}
/>
<TextBlock
text="Hello"
onTapped={e => {
MenuFlyout.ShowAt(_menuRef, {point: {x: x, y: 42}});
}}
ref={t => {
_tbRef.current = t;
}}>
<MenuFlyout
ref={m => {
_menuRef.current = m;
}}>
<MenuFlyoutItem text="menu option" />
</MenuFlyout>
</TextBlock>
WinUI controls are available in the WinUI
namespace.
const [visible, setVisible] = useState(Visibility.Visible);
// ...
<WinUI.InfoBar
message="the message"
title="the title"
isOpen={true}
visibility={visible}
onClosed={() => {
setVisible(Visibility.Collapsed);
}}
severity={WinUIEnums.InfoBarSeverity.Success}
/>