1# Creating a Custom Component
2
3
4In ArkUI, components are what's displayed on the UI. They can be classified as built-in components – those directly provided by the ArkUI framework, and custom components – those defined by developers. Defining the entire application UI with just built-in components would lead to a monolithic design, low code maintainability, and poor execution performance. A good UI is the result of a well-thought-out development process, with such factors as code reusability, separation of service logic from the UI, and version evolution carefully considered. Creating custom components that encapsulate the UI and some business logic is a critical step in this process.
5
6
7The custom component has the following features:
8
9
10- Combinable: allows you to combine built-in components and other components, as well as their attributes and methods.
11
12- Reusable: can be reused by other components and used as different instances in different parent components or containers.
13
14- Data-driven update: holds some state and triggers UI re-rendering with the change of state variables.
15
16## Basic Usage of Custom Components
17
18The following example shows the basic usage of a custom component.
19
20```ts
21@Component
22struct HelloComponent {
23  @State message: string = 'Hello, World!';
24
25  build() {
26    // The HelloComponent custom component combines the <Row> and <Text> built-in components.
27    Row() {
28      Text(this.message)
29        .onClick(() => {
30          // The change of the state variable message drives the UI to be re-rendered. As a result, the text changes from "Hello, World!" to "Hello, ArkUI!".
31          this.message = 'Hello, ArkUI!';
32        })
33    }
34  }
35}
36```
37> **NOTE**
38>
39> To reference the custom component in another file, use the keyword **export** to export the component and then use **import** to import it to the target file.
40
41Multiple **HelloComponent** instances can be created in the **build()** function of other custom components. In this way, **HelloComponent** is reused by those custom components.
42
43```ts
44@Entry
45@Component
46struct ParentComponent {
47  build() {
48    Column() {
49      Text('ArkUI message')
50      HelloComponent({ message: 'Hello World!' });
51      Divider()
52      HelloComponent({message: 'Hello, World!'});
53    }
54  }
55}
56```
57
58
59To fully understand the preceding example, a knowledge of the following concepts is essential:
60
61
62- [Basic Structure of a Custom Component](#basic-structure-of-a-custom-component)
63
64- [Member Functions/Variables](#member-functionsvariables)
65
66- [Rules for Custom Component Parameters](#rules-for-custom-component-parameters)
67
68- [build Function](#build-function)
69
70- [Universal Style of a Custom Component](#universal-style-of-a-custom-component)
71
72
73## Basic Structure of a Custom Component
74
75- struct: The definition of a custom component must start with the \@Component struct followed by the component name, and then component body enclosed by curly brackets {....}. No inheritance is allowed. You can omit the **new** operator when instantiating a struct.
76  > **NOTE**
77  >
78  > The name or its class or function name of a custom component must be different from that of any built-in components.
79
80- \@Component: The \@Component decorator can decorate only the structs declared by the **struct** keyword. When being decorated by \@Component, a struct has the componentization capability. You must implement the **build** function for it to describe the UI. Each struct can be decorated by only one \@Component. \@Component can accept an optional parameter of the Boolean type.
81  > **NOTE**
82  >
83  > This decorator can be used in ArkTS widgets since API version 9.
84  >
85  > An optional parameter of the Boolean type can be used in the \@Component since API version 11.
86
87  ```ts
88  @Component
89  struct MyComponent {
90  }
91  ```
92
93  ### freezeWhenInactive<sup>11+</sup>
94  Describes the [custom component freezing](arkts-custom-components-freeze.md) option.
95
96  | Name  | Type  | Mandatory| Description                                                        |
97  | ------ | ------ | ---- | ------------------------------------------------------------ |
98  | freezeWhenInactive | bool | No| Whether to enable the component freezing.|
99
100  ```ts
101  @Component({ freezeWhenInactive: true })
102  struct MyComponent {
103  }
104  ```
105
106- build(): The **build()** function is used to define the declarative UI description of a custom component. Every custom component must define a **build()** function.
107
108  ```ts
109  @Component
110  struct MyComponent {
111    build() {
112    }
113  }
114  ```
115
116- \@Entry: A custom component decorated with \@Entry is used as the default entry component of the page. Only one component can be decorated with \@Entry in a single page. The \@Entry decorator accepts an optional parameter of type [LocalStorage](arkts-localstorage.md).
117
118  > **NOTE**
119  >
120  > This decorator can be used in ArkTS widgets since API version 9.
121  >
122  > Since API version 10, the \@Entry decorator accepts an optional parameter of type [LocalStorage](arkts-localstorage.md) or type [EntryOptions](#entryOptions).
123  >
124  > This decorator can be used in atomic services since API version 11.
125
126  ```ts
127  @Entry
128  @Component
129  struct MyComponent {
130  }
131  ```
132
133  ### EntryOptions<sup>10+</sup>
134
135  Describes the named route options.
136
137  | Name  | Type  | Mandatory| Description                                                        |
138  | ------ | ------ | ---- | ------------------------------------------------------------ |
139  | routeName | string | No| Name of the target named route.|
140  | storage | [LocalStorage](arkts-localstorage.md) | No| Storage of the page-level UI state.|
141  | useSharedStorage<sup>12+</sup> | boolean | No| Whether to use the [LocalStorage](arkts-localstorage.md) object returned by the **LocalStorage.getShared()** API.<br>Default value: **false**|
142
143  > **NOTE**
144  >
145  > When **useSharedStorage** is set to **true** and **storage** is assigned a value, the value of **useSharedStorage** has a higher priority.
146
147  ```ts
148  @Entry({ routeName : 'myPage' })
149  @Component
150  struct MyComponent {
151  }
152  ```
153
154
155- \@Reusable: Custom components decorated by \@Reusable can be reused.
156
157  > **NOTE**
158  >
159  > This decorator can be used in ArkTS widgets since API version 10.
160
161  ```ts
162  @Reusable
163  @Component
164  struct MyComponent {
165  }
166  ```
167
168
169## Member Functions/Variables
170
171In addition to the mandatory **build()** function, a custom component may implement other member functions with the following restrictions:
172
173
174- Access to the member functions is private. Avoid declaring the member functions as static functions.
175
176
177A custom component can also implement member variables with the following restrictions:
178
179
180- Access to the member variables is private. Avoid declaring the member variables as static variables.
181
182- Local initialization is optional for some member variables and mandatory for others. For details about whether local initialization or initialization from the parent component is required, see [State Management](arkts-state-management-overview.md).
183
184
185## Rules for Custom Component Parameters
186
187As can be learnt from preceding examples, a custom component can be created from a **build** method. During the creation, the custom component's parameters are initialized based on the decorator rules.
188
189
190```ts
191@Component
192struct MyComponent {
193  private countDownFrom: number = 0;
194  private color: Color = Color.Blue;
195
196  build() {
197  }
198}
199
200@Entry
201@Component
202struct ParentComponent {
203  private someColor: Color = Color.Pink;
204
205  build() {
206    Column() {
207      // Create an instance of MyComponent and initialize its countDownFrom variable with the value 10 and its color variable with the value this.someColor.
208      MyComponent({ countDownFrom: 10, color: this.someColor })
209    }
210  }
211}
212```
213
214In the following example, a function in the parent component is passed to the child component and called therein.
215
216```ts
217@Entry
218@Component
219struct Parent {
220  @State cnt: number = 0
221  submit: () => void = () => {
222    this.cnt++;
223  }
224
225  build() {
226    Column() {
227      Text(`${this.cnt}`)
228      Son({ submitArrow: this.submit })
229    }
230  }
231}
232
233@Component
234struct Son {
235  submitArrow?: () => void
236
237  build() {
238    Row() {
239      Button('add')
240        .width(80)
241        .onClick(() => {
242          if (this.submitArrow) {
243            this.submitArrow()
244          }
245        })
246    }
247    .justifyContent(FlexAlign.SpaceBetween)
248    .height(56)
249  }
250}
251```
252
253## build() Function
254
255Whatever declared in the **build()** function are called UI descriptions. UI descriptions must comply with the following rules:
256
257- For an \@Entry decorated custom component, exactly one root component is required under the **build()** function. This root component must be a container component. **ForEach** is not allowed at the top level.
258  For an \@Component decorated custom component, exactly one root component is required under the **build()** function. This root component is not necessarily a container component. **ForEach** is not allowed at the top level.
259
260  ```ts
261  @Entry
262  @Component
263  struct MyComponent {
264    build() {
265      // Exactly one root component is required, and it must be a container component.
266      Row() {
267        ChildComponent()
268      }
269    }
270  }
271
272  @Component
273  struct ChildComponent {
274    build() {
275      // Exactly one root component is required, and it is not necessarily a container component.
276      Image('test.jpg')
277    }
278  }
279  ```
280
281- Local variable declaration is not allowed. The following example should be avoided:
282
283  ```ts
284  build() {
285    // Avoid: declaring a local variable.
286    let a: number = 1;
287  }
288  ```
289
290- **console.info** can be used in the UI description only when it is in a method or function. The following example should be avoided:
291
292  ```ts
293  build() {
294    // Avoid: using console.info directly in UI description.
295    console.info('print debug log');
296  }
297  ```
298
299- Creation of a local scope is not allowed. The following example should be avoided:
300
301  ```ts
302  build() {
303    // Avoid: creating a local scope.
304    {
305      ...
306    }
307  }
308  ```
309
310- Only methods decorated by \@Builder can be called. The parameters of built-in components can be the return values of TS methods.
311
312  ```ts
313  @Component
314  struct ParentComponent {
315    doSomeCalculations() {
316    }
317
318    calcTextValue(): string {
319      return 'Hello World';
320    }
321
322    @Builder doSomeRender() {
323      Text(`Hello World`)
324    }
325
326    build() {
327      Column() {
328        // Avoid: calling a method not decorated by @Builder.
329        this.doSomeCalculations();
330        // Prefer: Call an @Builder decorated method.
331        this.doSomeRender();
332        // Prefer: Pass the return value of a TS method as the parameter.
333        Text(this.calcTextValue())
334      }
335    }
336  }
337  ```
338
339- The **switch** syntax is not allowed. Use **if** instead. The following is an example:
340
341  ```ts
342  build() {
343    Column() {
344      // Avoid: using the switch syntax.
345      switch (expression) {
346        case 1:
347          Text('...')
348          break;
349        case 2:
350          Image('...')
351          break;
352        default:
353          Text('...')
354          break;
355      }
356      // Correct usage: Use if.
357      if(expression == 1) {
358        Text('...')
359      } else if(expression == 2) {
360        Image('...')
361      } else {
362        Text('...')
363      }
364    }
365  }
366  ```
367
368- Expressions are not allowed. The following example should be avoided:
369
370  ```ts
371  build() {
372    Column() {
373      // Avoid: expressions.
374      (this.aVar > 10) ? Text('...') : Image('...')
375    }
376  }
377  ```
378
379- Directly changing a state variable is not allowed. The following example should be avoided: For details, see [State Variables Modification in build() Is Forbidden](./arkts-state.md#state-variables-modification-in-build()-is-forbidden).
380
381  ```ts
382  @Component
383  struct CompA {
384    @State col1: Color = Color.Yellow;
385    @State col2: Color = Color.Green;
386    @State count: number = 1;
387    build() {
388      Column() {
389        // Avoid: directly changing the value of count in the <Text> component.
390        Text(`${this.count++}`)
391          .width(50)
392          .height(50)
393          .fontColor(this.col1)
394          .onClick(() => {
395            this.col2 = Color.Red;
396          })
397        Button("change col1").onClick(() =>{
398          this.col1 = Color.Pink;
399        })
400      }
401      .backgroundColor(this.col2)
402    }
403  }
404  ```
405
406  In ArkUI state management, UI re-render is driven by state.
407
408  ![en-us_image_0000001651365257](figures/en-us_image_0000001651365257.png)
409
410  Therefore, do not change any state variable in the **build()** or \@Builder decorated method of a custom component. Otherwise, loop rendering may result. Depending on the update mode (full update or minimum update), **Text('${this.count++}')** imposes different effects:
411
412  - Full update (API version 8 or before): ArkUI may fall into an infinite re-rendering loop because each rendering of the **Text** component changes the application state and causes a new round of re-renders. When **this.col2** is changed, the entire **build** function is executed. As a result, the text bound to **Text(${this.count++})** is also changed. Each time **Text(${this.count++})** is re-rendered, the **this.count** state variable is updated, and a new round of **build** execution follows, resulting in an infinite loop.
413  - Minimized update (API version 9 or later): When **this.col2** is changed, only the **Column** component is updated, and the **Text** component is not changed. When **this.col1** is changed, the entire **Text** component is updated and all of its attribute functions are executed. As a result, the value of **${this.count++}** in the **Text** component is changed. Currently, the UI is updated by component. If an attribute of a component changes, the entire component is updated. Therefore, the overall update link is as follows: **this.col1** = **Color.Pink** - > **Text** component re-render - > **this.count++** - > **Text** component re-render. It should be noted that this way of writing causes the **Text** component to be rendered twice during the initial render, which affects the performance.
414
415  The behavior of changing the application state in the **build** function may be more covert than that in the preceding example. The following are some examples:
416
417  - Changing the state variable within the \@Builder, \@Extend, or \@Styles decorated method
418
419  - Changing the application state variable in the function called during parameter calculation, for example, **Text('${this.calcLabel()}')**
420
421  - Modifying the current array: In the following code snippet, **sort()** changes the array **this.arr**, and the subsequent **filter** method returns a new array.
422
423    ```ts
424    // Avoid the usage below.
425    @State arr : Array<...> = [ ... ];
426    ForEach(this.arr.sort().filter(...),
427      item => {
428      ...
429    })
430    // Prefer: Call filter before sort() to return a new array. In this way, sort() does not change this.arr.
431    ForEach(this.arr.filter(...).sort(),
432      item => {
433      ...
434    })
435    ```
436
437## Universal Style of a Custom Component
438
439The universal style of a custom component is configured by the chain call.
440
441
442```ts
443@Component
444struct MyComponent2 {
445  build() {
446    Button(`Hello World`)
447  }
448}
449
450@Entry
451@Component
452struct MyComponent {
453  build() {
454    Row() {
455      MyComponent2()
456        .width(200)
457        .height(300)
458        .backgroundColor(Color.Red)
459    }
460  }
461}
462```
463
464> **NOTE**
465>
466> When ArkUI sets styles for custom components, an invisible container component is set for **MyComponent2**. These styles are set on the container component instead of the **Button** component of **MyComponent2**. As seen from the rendering result, the red background color is not directly applied to the button. Instead, it is applied to the container component that is invisible to users where the button is located.
467