1# Improving Layout Performance
2
3## Background
4
5Layouts in the user interface (UI) are a key part of interactions between users and your application. A UI with diverse, well-designed layouts is more enjoyable to interact with. Yet, if poorly implemented, layouts can also lead to performance bottlenecks. Poor layouts may still appear, on the surface, attractive, but the huge overhead underneath – resulting from transition layout calculation and unnecessary nesting, can undermine performance. This document provides tips for optimizing the layout structure, with primary focus on most common layouts and their use cases.
6
7## Common Layouts
8
9The layout defines how components are laid out in the UI. ArkUI offers a diverse array of layouts. Besides the basic layouts – [linear](../ui/arkts-layout-development-linear.md) ([Row](../reference/apis-arkui/arkui-ts/ts-container-row.md)/[Column](../reference/apis-arkui/arkui-ts/ts-container-column.md)), [stack](../ui/arkts-layout-development-stack-layout.md) ([Stack](../reference/apis-arkui/arkui-ts/ts-container-stack.md)), [flex](../ui/arkts-layout-development-flex-layout.md) ([Flex](../reference/apis-arkui/arkui-ts/ts-container-flex.md)), [relative](../ui/arkts-layout-development-relative-layout.md) ([RelativeContainer](../reference/apis-arkui/arkui-ts/ts-container-relativecontainer.md)), and [responsive grid](../ui/arkts-layout-development-grid-layout.md) ([GridCol](../reference/apis-arkui/arkui-ts/ts-container-gridcol.md)), you also have access to the advanced [list](../ui/arkts-layout-development-create-list.md) ([List](../reference/apis-arkui/arkui-ts/ts-container-list.md)), [grid](../ui/arkts-layout-development-create-grid.md) ([Grid](../reference/apis-arkui/arkui-ts/ts-container-grid.md)/[\<GridItem>](../reference/apis-arkui/arkui-ts/ts-container-griditem.md)), and [swiper](../ui/arkts-layout-development-create-looping.md) ([Swiper](../reference/apis-arkui/arkui-ts/ts-container-swiper.md)) layouts.
10
11## Optimizing Layout Structure
12
13### Reducing Nesting
14
15Compared with a flat layout, a deep layout with too much nesting means more time spent on node creation and layout. For optimized layout hierarchies, avoid redundant nesting or flatten the layout.
16
17**Avoiding Redundant Nesting**
18
19Redundant nesting results in unnecessary component nodes and deepens the hierarchy of the component tree. For example, if internal and external containers have the same layout direction, the external container can be used in place of the internal one to deliver the same layout effect. In this case, the redundant container can be removed to reduce nesting.
20
21Nonexample:
22
23The following uses the **Grid** component to implement a grid, outside of which three layers of stack containers with different attributes are deployed.
24
25```ts
26@Entry
27@Component
28struct AspectRatioExample12 {
29    @State children: Number[] = Array.from(Array<number>(900), (v, k) => k);
30
31    build() {
32      Scroll() {
33      Grid() {
34        ForEach(this.children, (item: Number[]) => {
35          GridItem() {
36            Stack() {
37              Stack() {
38                Stack() {
39                  Text(item.toString())
40                }.size({ width: "100%"})
41              }.backgroundColor(Color.Yellow)
42            }.backgroundColor(Color.Pink)
43          }
44        }, (item: string) => item)
45      }
46      .columnsTemplate('1fr 1fr 1fr 1fr')
47      .columnsGap(0)
48      .rowsGap(0)
49      .size({ width: "100%", height: "100%" })
50    }
51  }
52}
53
54```
55
56A review of the component tree structure reveals that, the desirable UI effect can be equally achieved by the attribute settings of the grid items. In this sense, the three-layer stack containers are redundant and can be removed.
57
58
59```
60└─┬Scroll
61  └─┬Grid
62    ├─┬GridItem
63    │ └─┬Stack
64    │   └─┬Stack
65    │     └─┬Stack
66    │       └──Text
67    ├──GridItem
68    ├──GridItem
69```
70
71Example:
72
73In the following code, by removing redundant nesting of stack containers, the number of child components in each grid item is three less than that in the previous example.
74
75```ts
76@Entry
77@Component
78struct AspectRatioExample11 {
79  @State children: Number[] = Array.from(Array<number>(900), (v, k) => k);
80
81  build() {
82    Scroll() {
83      Grid() {
84        ForEach(this.children, (item: Number[]) => {
85          GridItem() {
86            Text(item.toString())
87          }.backgroundColor(Color.Yellow)
88        }, (item: string) => item)
89      }
90      .columnsTemplate('1fr 1fr 1fr 1fr')
91      .columnsGap(0)
92      .rowsGap(0)
93      .size({ width: "100%", height: "100%" })
94    }
95  }
96}
97```
98
99The component tree structure is as follows:
100
101```
102└─┬Scroll
103  └─┬Grid
104    ├─┬GridItem
105    │ └──Text
106    ├──GridItem
107    ├──GridItem
108```
109
110**Flattening Layout**
111
112In implementing an adaptive layout, use of the **Flex** component can cause multi-level nesting. In this scenario, you are advised to use **RelativeContainer** to flatten the layout to effectively reduce nesting and shorten the component creation time.
113
114The figure below shows an adaptive UI.
115
116![layout-ui-view](figures/layout-ui-view.png)
117
118Nonexample:
119
120The following code uses a linear layout to implement the UI shown above:
121
122```ts
123@Entry
124@Component
125struct MyComponent {
126  build() {
127    Row() {
128      Column() {
129        Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
130          Text ('J')
131          // For details about the attribute parameters, see the example.
132        }
133        .width("40vp")
134        .height("40vp")
135      }.height("100%").justifyContent(FlexAlign.Center)
136      //body
137      Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Start }) {
138          Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center }) {
139          Flex({ direction: FlexDirection.Row,
140            justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
141            //Phone number or first name
142            Text('John')
143             // For details about the attribute parameters, see the example.
144
145            //Date Time
146            Text ('2 min ago')
147             // For details about the attribute parameters, see the example.
148             }
149          .width("100%").height(22)
150
151          Row() {
152            Text() {
153              //Content Abbreviations for Latest News
154              Span('Hello World'.replace(new RegExp("/[\r\n]/g"), " "))
155                .fontSize("14fp")
156                .fontColor('# 66182431')
157            }
158            .maxLines(1)
159            .textOverflow({ overflow: TextOverflow.Ellipsis })
160          }
161          .alignSelf(ItemAlign.Start)
162          .alignItems(VerticalAlign.Top)
163          .width("100%")
164          .height(19)
165          .margin({ top: "2vp" })
166        }.width("100%")
167        .height("100%")
168      }
169      .layoutWeight(1)
170      .height("100%")
171      .padding({ left: "12vp" })
172    }
173    .alignItems(VerticalAlign.Top)
174    .width("100%")
175    .height("100%")
176  }
177}
178```
179
180The component tree structure is as follows:
181
182```
183└─┬Row
184  ├──┬Column
185  │  └─┬Flex
186  │    └──Text
187  └─┬Flex
188    └─┬Flex
189      │ └─┬Flex
190      │   ├──Text
191      │   └──Text
192      └─┬Row
193        └──Text
194```
195
196To place four elements in proper positions, 11 components are used with a tree depth of 5, which is actually unreasonable.
197
198The figure below illustrates the layout of the elements.
199
200![Input picture description](figures/layout-relative-view.png)
201
202Example:
203
204The figure above shows a clear relative layout relationship. In light of this, a **RelativeContainer** can be adopted to improve performance. Below is the specific code implementation.
205
206```ts
207@Entry
208@Component
209struct MyComponent {
210  build() {
211    Row() {
212      RelativeContainer() {
213        Text ('J')
214          .fontSize('20.0vp')
215          .fontWeight(FontWeight.Bold)
216          .fontColor(Color.White)
217          .height('40vp')
218          .width('40vp')
219          .textAlign(TextAlign.Center)
220          .clip(new Circle({ width: '40vp', height: '40vp' }))
221          .backgroundColor(Color.Green)
222          .alignRules({
223            center: { anchor: "__container__", align: VerticalAlign.Center },
224            left: { anchor: "__container__", align: HorizontalAlign.Start }
225          })
226          .id('head')
227        Text('John')
228          .fontSize('16.0fp')
229          .textOverflow({ overflow: TextOverflow.Ellipsis })
230          .fontColor('# ff182431')
231          .maxLines(1)
232          .fontWeight(FontWeight.Medium)
233          .padding({ left: '12vp' })
234          .height(22)
235          .alignRules({
236            top: { anchor: 'head', align: VerticalAlign.Top },
237            left: { anchor: 'head', align: HorizontalAlign.End }
238          })
239          .id('name')
240        Text ('2 min ago')
241          .fontColor('# 66182431')
242          .fontSize('12fp')
243          .maxLines(1)
244          .height(22)
245          .alignRules({
246            top: { anchor: 'head', align: VerticalAlign.Top },
247            right: { anchor: '__container__', align: HorizontalAlign.End }
248          })
249          .id("time")
250        Text() {
251          //Content Abbreviations for Latest News
252          Span('Hello World'.replace(new RegExp("/[\r\n]/g"), " "))
253            .fontSize('14fp')
254            .fontColor('# 66182431')
255        }
256        .maxLines(1)
257        .textOverflow({ overflow: TextOverflow.Ellipsis })
258        .width('100%')
259        .height(19)
260        .margin({ top: '2vp' })
261        .padding({ left: '12vp' })
262        .alignRules({
263          top: { anchor: 'name', align: VerticalAlign.Bottom },
264          left: { anchor: 'head', align: HorizontalAlign.End }
265        })
266        .id('content')
267      }
268      .width('100%').height('100%')
269      .border({ width: 1, color: "# 6699FF" })
270    }
271    .height('100%')
272  }
273}
274```
275
276The new layout, which delivers the same effect as the previous deep layout, has three less component layers and six less components.
277
278```
279└─┬RelativeContainer
280  ├──Text
281  ├──Text
282  ├──Text
283  └──Text
284```
285
286As shown in the preceding examples, the flattened layout presents clearer logic design, avoiding components that are not involved in drawing for better performance and less memory usage. By flattening the layout, you collapse the hierarchy levels of a deep UI tree and re-arrange content into under one top node. The figure below illustrates how layout flattening helps remove the redundant layout nodes.
287
288![layout-relative-introduce](figures/layout-relative-introduce.png)
289
290Tools available for flattening the layout include, among others, the [RelativeContainer](../reference/apis-arkui/arkui-ts/ts-container-relativecontainer.md) component, [Grid](../reference/apis-arkui/arkui-ts/ts-container-grid.md) component, and [absolute location](../reference/apis-arkui/arkui-ts/ts-universal-attributes-location.md).
291
292### Using High-Performance Layout Components
293
294**Replacing Flex Containers with Columns/Rows**
295
296If you are using the flex container to merely implement a horizontal or vertical layout, the **Row** and **Column** components are better choices in terms of rendering performance benefits. For details about the impact of the flex container on performance, see [Flex Layout Performance Improvement](flex-development-performance-boost.md).
297
298For details about how to replace the flex container components with **Row** and **Column** components to avoid secondary rendering, see [More Performance Improvement Methods](arkts-performance-improvement-recommendation.md).
299
300**Reducing Use of if/else**
301
302In the build function of ArkUI, **if/else** is also regarded as a component and a node in the component tree. In scenarios where the basic layout remains unchanged, reduce the use of if/else if modifying attribute settings can achieve the same purpose – present different GUI content under different conditions. The use of **if/else** not only adds a layer of nodes, but might also result in re-layout and re-render.
303
304Nonexample:
305
306In the following code, the value of **isVisible** is used to control the visibility of the **Image** component. As a result, the **Image** component is continuously created and destroyed during the selection switchover.
307
308```ts
309@Entry
310@Component
311struct TopicItem {
312  @State isVisible : Boolean = true;
313
314  build() {
315    Stack() {
316      Column(){
317        if (this.isVisible) {
318          Image($r('app.media.icon')).width('25%').height('12.5%')
319          Image($r('app.media.icon')).width('25%').height('12.5%')
320          Image($r('app.media.icon')).width('25%').height('12.5%')
321          Image($r('app.media.icon')).width('25%').height('12.5%')
322        }
323      }
324      Column() {
325        Row().width(300).height(200).backgroundColor(Color.Pink)
326      }
327    }
328  }
329}
330```
331
332Below are the component trees at different values of **isVisible**.
333
334```
335Component tree when **isVisible** is **true**:
336└─┬Stack
337  ├─┬Column
338  │ ├──Image
339  │ ├──Image
340  │ ├──Image
341  │ └──Image
342  └─┬Column
343    └──Row
344
345Component tree when **isVisible** is **false**:
346└─┬Stack
347  ├──Column
348  └─┬Column
349    └──Row
350```
351
352Example:
353
354In the following example, the **visibility** attribute is used to control the visibility of the **Image** component, avoiding the re-layout and re-rendered caused by **if/else**.
355
356```ts
357@Entry
358@Component
359struct TopicItem {
360  @State isVisible : Boolean = true;
361
362  build() {
363    Stack() {
364      Column(){
365          Image($r('app.media.icon'))
366            .width('25%').height('12.5%').visibility(this.isVisible ? Visibility.Visible : Visibility.None)
367          Image($r('app.media.icon'))
368            .width('25%').height('12.5%').visibility(this.isVisible ? Visibility.Visible : Visibility.None)
369          Image($r('app.media.icon'))
370            .width('25%').height('12.5%').visibility(this.isVisible ? Visibility.Visible : Visibility.None)
371          Image($r('app.media.icon'))
372            .width('25%').height('12.5%').visibility(this.isVisible ? Visibility.Visible : Visibility.None)
373      }
374      Column() {
375        Row().width(300).height(200).backgroundColor(Color.Pink)
376      }
377    }
378  }
379}
380```
381
382Note: The aforementioned recommendation is provided in regard of performance. If memory is more of a concern, use **if/else** instead.
383
384## Reducing Layout Time
385
386Compared with a flat layout, a deep layout with too much nesting means more time spent on node creation and layout. For optimized layout hierarchies, avoid redundant nesting or flatten the layout.
387
388Nonexample:
389
390The following uses a linear layout, which takes 166 ms 313 us.
391
392```ts
393
394import measure from '@ohos.measure';
395import prompt from '@ohos.prompt';
396
397@Entry
398@Component
399struct PerformanceRelative {
400  @State message: string = 'Hello World'
401  @State textWidth: string = "";
402
403  build() {
404    Column() {
405      Image($r("app.media.app_icon")).width("100%").height(300).margin({ bottom: 20 })
406      Row() {
407        Blank()
408        Column() {
409          Image($r("app.media.app_icon")).margin({ bottom: 4 }).width(40).aspectRatio(1)
410          Text("Name")
411        }.margin({ left: 8, right: 8 })
412      }.position({ y: 280 }).width("100%")
413      // Empty row
414      Row().height(this.textWidth)
415      Column() {
416        Row() {
417          Text("Singapore").fontSize(20).fontWeight(FontWeight.Bolder)
418            .margin(8)
419            .textAlign(TextAlign.Start)
420        }.width("100%").justifyContent(FlexAlign.Start)
421
422        Flex({ alignItems: ItemAlign.Center }) {
423          Text("Camera").flexShrink(0)
424            .margin({ right: 8 })
425          TextInput()
426        }.margin(8)
427
428        Flex({ alignItems: ItemAlign.Center }) {
429          Text("Settings").flexShrink(0)
430            .margin({ right: 8 })
431          TextInput()
432        }.margin(8)
433
434        Row() {
435          Column() {
436            Image($r("app.media.app_icon")).width(80).aspectRatio(1).margin({ bottom: 8 })
437            Text("Description")
438          }.margin(8)
439
440          Column() {
441            Text("Title").fontWeight(FontWeight.Bold).margin({ bottom: 8 })
442            Text("Long Text")
443          }.margin(8).layoutWeight(1).alignItems(HorizontalAlign.Start)
444        }.margin(8).width("100%").alignItems(VerticalAlign.Top)
445      }.layoutWeight(1)
446
447      Flex({ justifyContent: FlexAlign.End }) {
448        Button("Upload").margin(8)
449        Button("Discard").margin(8)
450      }
451    }
452    .width("100%").height("100%")
453  }
454}
455
456```
457
458Example:
459
460Use of a relative layout reduces both the nesting depth and quantity of components, shortening the time required for layout to 123 ms 278 us.
461
462```ts
463
464@Entry
465@Component
466struct RelativePerformance {
467  @State message: string = 'Hello World'
468
469  build() {
470    RelativeContainer(){
471      Image($r("app.media.app_icon"))
472        .height(300)
473        .width("100%")
474        .id("topImage")
475        .alignRules({
476          left: { anchor: "__container__", align: HorizontalAlign.Start },
477          top: {anchor: "__container__", align: VerticalAlign.Top }
478        })
479      Image($r("app.media.app_icon"))
480        .width(40)
481        .aspectRatio(1)
482        .margin(4)
483        .id("topCornerImage")
484        .alignRules({
485          right: { anchor: "__container__", align: HorizontalAlign.End },
486          center: {anchor: "topImage", align: VerticalAlign.Bottom }
487        })
488      Text("Name")
489        .id("name")
490        .margin(4)
491        .alignRules({
492          left: { anchor: "topCornerImage", align: HorizontalAlign.Start },
493          top: {anchor: "topCornerImage", align: VerticalAlign.Bottom }
494        })
495      Text("Singapore")
496        .margin(8)
497        .fontWeight(FontWeight.Bolder)
498        .fontSize(20)
499        .id("singapore")
500        .alignRules({
501          left: { anchor: "__container__", align: HorizontalAlign.Start },
502          top: {anchor: "name", align: VerticalAlign.Bottom }
503        })
504      Text("Camera")
505        .margin(8)
506        .id("camera")
507        .alignRules({
508          left: { anchor: "__container__", align: HorizontalAlign.Start },
509          top: {anchor: "singapore", align: VerticalAlign.Bottom }
510        })
511      TextInput()
512        .id("cameraInput")
513        .alignRules({
514          left: { anchor: "camera", align: HorizontalAlign.End },
515          right:{ anchor: "__container__", align: HorizontalAlign.End },
516          top: {anchor: "camera", align: VerticalAlign.Top },
517          bottom: { anchor: "camera", align: VerticalAlign.Bottom }
518        })
519      Text("Settings")
520        .margin(8)
521        .id("settings")
522        .alignRules({
523          left: { anchor: "__container__", align: HorizontalAlign.Start },
524          top: {anchor: "camera", align: VerticalAlign.Bottom }
525        })
526      TextInput()
527        .id("settingInput")
528        .alignRules({
529          left: { anchor: "settings", align: HorizontalAlign.End },
530          right:{ anchor: "__container__", align: HorizontalAlign.End },
531          top: {anchor: "settings", align: VerticalAlign.Top },
532          bottom: { anchor: "settings", align: VerticalAlign.Bottom }
533        })
534      Image($r("app.media.app_icon"))
535        .id("descriptionIcon")
536        .margin(8)
537        .width(80)
538        .aspectRatio(1)
539        .alignRules({
540          left: { anchor: "__container__", align: HorizontalAlign.Start },
541          top: {anchor: "settings", align: VerticalAlign.Bottom }
542        })
543      Text("Description")
544        .id("description")
545        .margin(8)
546        .alignRules({
547          left: { anchor: "__container__", align: HorizontalAlign.Start },
548          top: {anchor: "descriptionIcon", align: VerticalAlign.Bottom }
549        })
550      Text("Title")
551        .fontWeight(FontWeight.Bold)
552        .id("title")
553        .margin(8)
554        .alignRules({
555          left: { anchor: "description", align: HorizontalAlign.End },
556          top: {anchor: "descriptionIcon", align: VerticalAlign.Top }
557        })
558      Text("Long Text")
559        .id("longText")
560        .margin(8)
561        .alignRules({
562          left: { anchor: "description", align: HorizontalAlign.End },
563          right: { anchor: "__container__", align: HorizontalAlign.End },
564          top: {anchor: "title", align: VerticalAlign.Bottom }
565        })
566      Button("Discard")
567        .id("discard")
568        .margin(8)
569        .alignRules({
570          right: { anchor: "__container__", align: HorizontalAlign.End },
571          bottom: {anchor: "__container__", align: VerticalAlign.Bottom }
572        })
573      Button("Upload")
574        .id("upload")
575        .margin(8)
576        .alignRules({
577          right: { anchor: "discard", align: HorizontalAlign.Start },
578          bottom: {anchor: "__container__", align: VerticalAlign.Bottom }
579        })
580    }.width("100%").height("100%")
581  }
582}
583
584```
585
586
587## Using Layout Optimization Tools
588
589[DevEco Studio](../quick-start/deveco-studio-user-guide-for-openharmony.md) has a built-in ArkUI Inspector tool, which you can use to inspect the UI display effect of your application running on a real device. With ArkUI Inspector, you can quickly identify layout and other UI-related issues, as well as inspect the layout relationships and attributes of components.
590