title | description | contributors | |
---|---|---|---|
Creating Custom Native Elements |
What to do when built-in elements don't meet your needs? |
|
When working with view markup in NativeScript, you interact with a set of pre-registered elements, such as GridLayout
, Button
, and Label
. These are commonly referred to as "view components."
You can also create custom native elements or extend existing ones to add specialized functionality to your application.
This guide demonstrates how to:
- Register custom view elements across all NativeScript flavors.
- Implement a simple custom view component, starting from a basic class definition:
import { View } from '@nativescript/core'
export class Checkbox extends View {
// implementation details
}
You could use Checkbox
within XML markup by providing any namespace prefix and the path to it.
<Page xmlns="http://schemas.nativescript.org/tns.xsd"
xmlns:custom="./checkbox">
<StackLayout>
<custom:Checkbox width="200" height="200" />
</StackLayout>
</Page>
The registration can occur anywhere the view is needed or commonly in app.component.ts
, the root bootstrap component, making it available for use anywhere in your application.
import { registerElement } from '@nativescript/angular'
import { Checkbox } from './checkbox'
registerElement('Checkbox', () => Checkbox)
You can now use it like anything else:
<StackLayout>
<Checkbox width="200" height="200" />
</StackLayout>
The registration can be done in the bootsrap file, commonly app.ts
. With Svelte, we use camelCase on elements where applicable.
import { registerNativeViewElement } from '@nativescript-community/svelte-native/dom'
import { Checkbox } from './checkbox'
registerNativeViewElement('checkbox', () => Checkbox)
You can now use it like anything else:
<stackLayout>
<checkbox width="200" height="200" />
</stackLayout>
The registration can be done in the bootsrap file, commonly app.ts
. With React, we use camelCase on elements where applicable.
import { registerElement } from 'react-nativescript'
import { Checkbox } from './checkbox'
registerElement('checkbox', () => Checkbox)
You can now use it like anything else:
<stackLayout>
<checkbox width="200" height="200" />
</stackLayout>
The registration can be done in the bootsrap file, commonly app.tsx
.
import { registerElement } from 'dominative'
import { Checkbox } from './checkbox'
registerElement('checkbox', Checkbox)
You can now use it like anything else:
<stacklayout>
<checkbox width="200" height="200" />
</stacklayout>
The registration can be done in the bootsrap file, commonly app.ts
.
import { registerElement } from 'nativescript-vue'
import { Checkbox } from './checkbox'
registerElement('Checkbox', () => Checkbox)
You can now use it like anything else:
<StackLayout>
<Checkbox width="200" height="200" />
</StackLayout>
The process for creating new NativeScript view components is consistent across Angular, React, Solid, Svelte, TypeScript, and Vue.
Every custom NativeScript view must have:
- (required) A class extending any NativeScript View.
- (required)
createNativeView
: Construct and return a platform-native view. Note: It's only required if you want to override what's being returned from the extended class. - (optional)
initNativeView
: Perform initialization after creation. - (optional)
disposeNativeView
: Cleanup resources when removed.
Example:
import { View } from '@nativescript/core'
export class CustomView extends View {
createNativeView() {
// return UIView or android.view.View instance
}
initNativeView() {
// initialization code
}
disposeNativeView() {
// cleanup code
}
}
You can extend any existing NativeScript view, for example:
import { ContentView, Label, Button } from '@nativescript/core'
export class CustomView1 extends ContentView {}
export class CustomView2 extends Label {}
export class CustomView3 extends Button {}
This method must return an instance of the native view:
// iOS
createNativeView() {
return new WKWebView({ frame: CGRectZero, configuration });
}
// Android
createNativeView() {
return new android.webkit.WebView(this._context);
}
initNativeView
: Perform setup after the view has been created.disposeNativeView
: Free resources as needed.
Explore more examples within NativeScript core and plugins:
- Button implementation (iOS)
- Button implementation (Android)
- Custom Flutter view example (iOS)
- Custom Flutter view example (Android)
For additional details on advanced topics like using Cocoapods or Gradle, refer to this blog series.
Create a dedicated folder for your component, such as:
./checkbox
common.ts
index.android.ts
index.ios.ts
index.d.ts
This structure encapsulates platform-specific implementations and shared logic, simplifying imports and registration:
import { Checkbox } from './checkbox'
Let's create a <Checkbox>
component that behaves consistently on iOS and Android like this:
See the full working example on StackBlitz:
- Angular: https://stackblitz.com/edit/nativescript-create-custom-elements-checkbox?file=src%2Fapp%2Fcheckbox%2Fcommon.ts
- React: https://stackblitz.com/edit/nativescript-create-custom-elements-checkbox-react?file=src%2Fcomponents%2FScreenOne.tsx
- Solid: https://stackblitz.com/edit/nativescript-create-custom-elements-checkbox-solid?file=src%2Fcomponents%2Fhome.tsx
- Svelte: https://stackblitz.com/edit/nativescript-create-custom-elements-checkbox-svelte?file=app%2Fcomponents%2FHome.svelte
- TypeScript: https://stackblitz.com/edit/nativescript-create-custom-elements-checkbox-ts?file=app%2Fmain-page.xml
- Vue: https://stackblitz.com/edit/nativescript-create-custom-elements-checkbox-vue?file=src%2Fcomponents%2FHome.vue
In NativeScript, creating custom UI components can be straightforward and powerful. In this guide, you'll learn how to build a simple, reusable Checkbox component using only built-in modules from @nativescript/core
. We'll leverage a combination of GridLayout
, Label
, and Material Design icons.
The base structure of our Checkbox will utilize GridLayout
, which serves as a container for the checkbox icon:
import { GridLayout } from '@nativescript/core'
export class CheckboxCommon extends GridLayout {}
Next, we'll add a Label to serve as our checkbox icon, using Material Design icons for the visual representation.
import { GridLayout, Label, Color } from '@nativescript/core'
export class CheckboxCommon extends GridLayout {
checked = false
defaultColor = new Color('#dddddd')
selectionColor = new Color('#4CAF50')
private _iconLabel: Label
private _circleIcon = String.fromCharCode(0xf764)
private _checkmarkIcon = String.fromCharCode(0xf5e0)
constructor() {
super()
this.horizontalAlignment = 'center'
this.verticalAlignment = 'middle'
this._iconLabel = new Label()
this._iconLabel.text = this._circleIcon
this._iconLabel.className = 'mat'
this._iconLabel.color = this.defaultColor
this._iconLabel.horizontalAlignment = 'center'
this._iconLabel.verticalAlignment = 'middle'
this.addChild(this._iconLabel)
}
}
Include Material Design Icons in your project by adding this CSS class in your app.css
:
.mat {
font-family: 'Material Design Icons', 'MaterialDesignIcons';
font-weight: 400;
}
Ensure you've added the MaterialDesignIcons font file to your app's fonts
directory.
To enhance user interaction, add a simple scaling animation when toggling the checkbox state:
import { CoreTypes } from '@nativescript/core'
export class CheckboxCommon extends GridLayout {
// existing properties and constructor...
private _animateCheckmark() {
if (!this.checked) {
this._iconLabel.color = this.defaultColor
}
this._iconLabel
.animate({
scale: { x: 0.5, y: 0.5 },
duration: 200,
curve: CoreTypes.AnimationCurve.easeInOut,
})
.then(() => {
this._iconLabel.text = this.checked
? this._checkmarkIcon
: this._circleIcon
if (this.checked) {
this._iconLabel.color = this.selectionColor
}
this._iconLabel.animate({
scale: { x: 1, y: 1 },
duration: 200,
curve: CoreTypes.AnimationCurve.easeInOut,
})
})
}
}
NativeScript's Property
system allows you to easily expose customizable attributes:
const sizeProperty = new Property<CheckboxCommon, number>({
name: 'size',
defaultValue: 24,
affectsLayout: __APPLE__,
})
const checkedProperty = new Property<CheckboxCommon, boolean>({
name: 'checked',
defaultValue: false,
valueConverter: booleanConverter,
})
const defaultColorProperty = new Property<CheckboxCommon, Color>({
name: 'defaultColor',
equalityComparer: Color.equals,
valueConverter: (v) => new Color(v),
})
const selectionColorProperty = new Property<CheckboxCommon, Color>({
name: 'selectionColor',
equalityComparer: Color.equals,
valueConverter: (v) => new Color(v),
})
These properties automatically handle conversions, such as strings to colors or booleans.
Link these properties to your checkbox component:
export class CheckboxCommon extends GridLayout {
// existing implementation...
[sizeProperty.setNative](value: number) {
this._iconLabel.fontSize = value
}
[checkedProperty.setNative](value: boolean) {
this.checked = value
if (this.isLoaded) {
this._animateCheckmark()
}
}
[defaultColorProperty.setNative](value: Color) {
this.defaultColor = value
if (this._iconLabel) {
this._iconLabel.color = this.defaultColor
}
}
[selectionColorProperty.setNative](value: Color) {
this.selectionColor = value
}
}
// Register properties
sizeProperty.register(CheckboxCommon)
checkedProperty.register(CheckboxCommon)
defaultColorProperty.register(CheckboxCommon)
selectionColorProperty.register(CheckboxCommon)
Explore and interact with the complete example directly in the flavor you love:
- Angular: https://stackblitz.com/edit/nativescript-create-custom-elements-checkbox?file=src%2Fapp%2Fcheckbox%2Fcommon.ts
- React: https://stackblitz.com/edit/nativescript-create-custom-elements-checkbox-react?file=src%2Fcomponents%2FScreenOne.tsx
- Solid: https://stackblitz.com/edit/nativescript-create-custom-elements-checkbox-solid?file=src%2Fcomponents%2Fhome.tsx
- Svelte: https://stackblitz.com/edit/nativescript-create-custom-elements-checkbox-svelte?file=app%2Fcomponents%2FHome.svelte
- TypeScript: https://stackblitz.com/edit/nativescript-create-custom-elements-checkbox-ts?file=app%2Fmain-page.xml
- Vue: https://stackblitz.com/edit/nativescript-create-custom-elements-checkbox-vue?file=src%2Fcomponents%2FHome.vue
Creating custom components in NativeScript allows you to:
- Build complex components from simple elements
- Utilize powerful styling and animations
- Easily manage and expose customizable properties
Whether creating fully native views or hybrid custom components, NativeScript provides flexibility and efficiency to meet your application's needs.
Rather than create new elements from scratch, you may want to just adjust existing view elements. Learn about how to extend any element for custom behavior here.