Skip to content

Commit 83e2347

Browse files
committed
feat: custom native element docs
1 parent 8c7df95 commit 83e2347

File tree

3 files changed

+433
-0
lines changed

3 files changed

+433
-0
lines changed
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
---
2+
title: Creating Custom Native Elements
3+
description: What to do when built-in elements don't meet your needs? You can create new custom native elements or extend existing ones to enhance behavior for all sorts of cases.
4+
contributors:
5+
- NathanWalker
6+
---
7+
8+
When working on view markup with NativeScript, a collection of elements you interact with are registered for you like [GridLayout](https://docs.nativescript.org/ui/grid-layout), [Button](https://docs.nativescript.org/ui/button), [Label](https://docs.nativescript.org/ui/label), etc. These are just commonly used elements.
9+
10+
At anytime, you can create your own, extend others and fully customize views for your needs.
11+
12+
Let's first look at how you would register new elements across all flavors and then we'll discuss how to build one, starting with a simple example of a custom View class:
13+
14+
```ts
15+
import { View } from '@nativescript/core'
16+
17+
export class Checkbox extends View {
18+
// impl
19+
}
20+
```
21+
22+
## Registering New Elements
23+
24+
<Tabs>
25+
<Tab flavor="typescript">
26+
27+
You could use `Checkbox` within XML markup by providing any namespace prefix and the path to it.
28+
29+
```xml
30+
<Page xmlns="http://schemas.nativescript.org/tns.xsd"
31+
xmlns:custom="./checkbox">
32+
<StackLayout>
33+
<custom:Checkbox width="200" height="200" />
34+
</StackLayout>
35+
</Page>
36+
```
37+
38+
</Tab>
39+
<Tab flavor="angular">
40+
41+
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.
42+
43+
```ts
44+
import { registerElement } from '@nativescript/angular'
45+
import { Checkbox } from './checkbox'
46+
47+
registerElement('Checkbox', () => Checkbox)
48+
```
49+
50+
You can now use it like anything else:
51+
52+
```html
53+
<StackLayout>
54+
<Checkbox width="200" height="200" />
55+
</StackLayout>
56+
```
57+
58+
</Tab>
59+
<Tab flavor="svelte">
60+
61+
The registration can be done in the bootsrap file, commonly `app.ts`. With Svelte, we use camelCase on elements where applicable.
62+
63+
```ts
64+
import { registerNativeViewElement } from '@nativescript-community/svelte-native/dom'
65+
import { Checkbox } from './checkbox'
66+
67+
registerNativeViewElement('checkbox', () => Checkbox)
68+
```
69+
70+
You can now use it like anything else:
71+
72+
```html
73+
<stackLayout>
74+
<checkbox width="200" height="200" />
75+
</stackLayout>
76+
```
77+
78+
</Tab>
79+
<Tab flavor="react">
80+
81+
The registration can be done in the bootsrap file, commonly `app.ts`. With React, we use camelCase on elements where applicable.
82+
83+
```ts
84+
import { registerElement } from 'react-nativescript';
85+
import { Checkbox } from './checkbox'
86+
87+
registerElement('checkbox', () => Checkbox)
88+
```
89+
90+
You can now use it like anything else:
91+
92+
```html
93+
<stackLayout>
94+
<checkbox width="200" height="200" />
95+
</stackLayout>
96+
```
97+
98+
</Tab>
99+
<Tab flavor="solid">
100+
101+
The registration can be done in the bootsrap file, commonly `app.tsx`.
102+
103+
```ts
104+
import { registerElement } from 'dominative';
105+
import { Checkbox } from './checkbox'
106+
107+
registerElement('checkbox', Checkbox);
108+
```
109+
110+
You can now use it like anything else:
111+
112+
```html
113+
<stacklayout>
114+
<checkbox width="200" height="200" />
115+
</stacklayout>
116+
```
117+
118+
</Tab>
119+
<Tab flavor="vue">
120+
121+
The registration can be done in the bootsrap file, commonly `app.ts`.
122+
123+
```ts
124+
import { registerElement } from 'nativescript-vue';
125+
import { Checkbox } from './checkbox'
126+
127+
registerElement('Checkbox', () => Checkbox);
128+
```
129+
130+
You can now use it like anything else:
131+
132+
```html
133+
<StackLayout>
134+
<Checkbox width="200" height="200" />
135+
</StackLayout>
136+
```
137+
138+
</Tab>
139+
</Tabs>
140+
141+
## Creating New Views
142+
143+
Creating a new view element for use across anything in NativeScript (Angular, React, Solid, Svelte, TypeScript by itself, Vue, etc) is exactly the same.
144+
145+
### Custom View Anatomy
146+
147+
There's only 2 **fundamental** required aspects to any custom NativeScript view component (with 2 _optional_ additions):
148+
149+
1. Extend any NativeScript View.
150+
2. (**required**) `createNativeView`: Construct and return any platform native view.
151+
3. (*optional*) `initNativeView`: Initialize anything.
152+
4. (*optional*) `disposeNativeView`: Cleanup anything.
153+
154+
Let's look at those within the context of an example:
155+
156+
```ts
157+
// 1. (required) Extend any NativeScript View
158+
export class CustomView extends View {
159+
160+
// 2. (required) Construct and return any platform native view
161+
createNativeView() {
162+
// return instance of UIView or android.view.View;
163+
}
164+
165+
// 3. (optional) initialize anything
166+
initNativeView() {
167+
168+
}
169+
170+
// 4. (optional) cleanup anything
171+
disposeNativeView() {
172+
173+
}
174+
}
175+
```
176+
177+
Let's explain each point respectively.
178+
179+
1. (required) extend any NativeScript View: **any** NativeScript view, for example these are all valid:
180+
181+
```ts
182+
import { ContentView, Label, Button } from '@nativescript/core'
183+
184+
export class CustomView1 extends ContentView {
185+
// impl
186+
}
187+
188+
export class CustomView2 extends Label {
189+
// impl
190+
}
191+
192+
export class CustomView3 extends Button {
193+
// impl
194+
}
195+
```
196+
197+
2. `createNativeView`: Quite literally create and return **any** platform native view, for example these are both valid:
198+
199+
```ts
200+
// iOS
201+
createNativeView() {
202+
return new WKWebView({
203+
frame: CGRectZero,
204+
configuration: configuration,
205+
});
206+
}
207+
// Android
208+
createNativeView() {
209+
return new android.webkit.WebView(this._context);
210+
}
211+
```
212+
213+
3. `initNativeView`: Initialize anything you'd like.
214+
215+
4. `disposeNativeView`: Destroy and cleanup anything if needed.
216+
217+
You will find many examples of this pattern throughout @nativescript/core, for example:
218+
219+
- [Button implementation for iOS](https://github.com/NativeScript/NativeScript/blob/96af6fa83e586a1c443c8b179701450d803e12aa/packages/core/ui/button/index.ios.ts#L21)
220+
- [Button implementation for Android](https://github.com/NativeScript/NativeScript/blob/96af6fa83e586a1c443c8b179701450d803e12aa/packages/core/ui/button/index.android.ts#L72)
221+
222+
All NativeScript plugins follow this exact same pattern as well, for example:
223+
224+
- [@nativescript/flutter for iOS](https://github.com/NativeScript/ui-kit/blob/dca883ada479a6d0982abbcb01a51f661d927812/packages/flutter/index.ios.ts#L40)
225+
- [@nativescript/flutter for Android](https://github.com/NativeScript/ui-kit/blob/dca883ada479a6d0982abbcb01a51f661d927812/packages/flutter/index.android.ts#L80)
226+
227+
You can also learn a lot about this with additional details such as using Cocoapods, Gradle and more in [this blog post series](https://blog.nativescript.org/create-a-custom-view-plugin-marquee-label/).
228+
229+
### Implementing within a Project
230+
231+
Implementing custom native elements at any moment within a project starts with creating a folder for your new element followed by the implementation. A common folder organization is as follows:
232+
233+
```bash
234+
./checkbox
235+
common.ts
236+
index.android.ts
237+
index.d.ts
238+
index.ios.ts
239+
```
240+
241+
This represents a new encapsulated custom native `<Checkbox />` element sharing common code via `common.ts` with each platform implementation represented via it's suffix. The `index.d.ts` is a convenience allowing one to simply import via the folder to register the element anywhere, for example:
242+
243+
```ts
244+
import { Checkbox } from './checkbox'
245+
246+
// register custom element using examples above for the flavor you're using
247+
```
248+
249+
## Customize Existing Elements?
250+
251+
Rather than create a new element from scratch, you may want to customize existing view elements. Learn about how to [extend any element for custom beavhior here](/guide/customizing-view-elements).

0 commit comments

Comments
 (0)