Frontend Development 12 min read

How to Boost HarmonyOS UI Performance with a C‑Based Rendering Bridge

This article examines four major performance bottlenecks when rendering UI on HarmonyOS—excessive view hierarchy, lengthy communication pipelines, poor list rendering, and secondary layout passes—and introduces a C‑language native interface solution that streamlines component integration, reduces overhead, and aligns HarmonyOS rendering with iOS and Android.

JD Cloud Developers
JD Cloud Developers
JD Cloud Developers
How to Boost HarmonyOS UI Performance with a C‑Based Rendering Bridge

Background

In the dynamic cross‑platform HarmonyOS solution article, we described how UI rendering on HarmonyOS uses system components for recursive rendering. While iOS and Android also rely on their native components, HarmonyOS suffers from four serious issues:

1. Excessive UI hierarchy

For example, a financial app’s "7‑day investment" element has 52 layers on HarmonyOS versus 30 on iOS, causing frame drops and stutter.

2. Lengthy communication process

JS runs in the built‑in V8 VM, ArkTS runs in Huawei’s Ark VM, and communication must pass through JS → C++ → ArkTS, incurring high cross‑language overhead.

3. Poor list rendering performance

Long‑list rendering is critical on iOS, Android, and HarmonyOS. Although Huawei offers several optimizations, third‑party frameworks still struggle with performance on complex list scenarios due to ArkUI’s design.

4. Secondary layout passes

After setting layout properties on HarmonyOS components, the system performs an additional layout pass, adding extra overhead.

New Solution Practice

1. Problem Analysis

UI hierarchy : Recursive rendering with system components requires wrapping each custom component in an extra container (e.g., a

Stack

), increasing depth.

<code>@Componentexport
struct RomaDiv {
    build(){
        Stack(){
            // Use wrapBuilder for recursion
            ForEach(this.childrenTags, (childrenTag) => {
                RomaComponentFactory.builder()
            })
        }
    }
}
</code>

Communication length : JS runs in V8, ArkTS in Ark VM, and data must cross multiple threads and language boundaries, inflating latency.

List rendering : HarmonyOS’s reactive system performs dependency collection similar to Vue; when data changes, recursive analysis and off‑screen node updates degrade performance.

Secondary layout : Although Yoga layout calculations happen before setting properties, HarmonyOS does not recognize this, leading to a second layout pass.

2. New Solution Overview

Huawei provides a C‑language imperative interface that sits between native UI components and the ArkTS layer. This C API bypasses state‑management overhead, avoids costly JS‑to‑C++‑to‑ArkTS type conversions, and delivers better performance.

Using the C interface, UI hierarchy aligns with iOS/Android, communication shortens to JS → C++, and list rendering can be controlled directly, allowing pre‑rendering and eliminating secondary layout.

3. How to Use

In a dynamic HarmonyOS cross‑platform project, ArkTS components may nest C components (and vice‑versa). The following demo shows complex nesting scenarios.

3.1 ArkTS Inserting C Component Example

The process consists of three steps:

Create a

NodeContent

manager.

Place a

ContentSlot

in the

build

function.

Create the native node via C API.

<code>import entry from 'libentry.so';
import { NodeContent } from '@ohos.arkui.node'

@Entry
@Component
struct CMixArkTS {
    // 1. NodeContent manager
    private divNodeContent: NodeContent = new NodeContent();

    build(){
        // 2. ContentSlot placeholder
        ContentSlot(this.divNodeContent);
    }

    aboutToAppear(): void {
        // 3. Create native node (CAPI)
        entry.CreateNativeDivNode(this.divNodeContent);
    }
}
</code>

The native C implementation of

CreateNativeDivNode

:

<code>// 1. C component – green border
static napi_value CreateNativeDivNode(napi_env env, napi_callback_info info) {
    if ((env == nullptr) || (info == nullptr)) {
        return nullptr;
    }
    napi_value returnVal = nullptr;
    size_t argc = 1;
    napi_value args[1] = {nullptr};
    if (napi_get_cb_info(env, info, &amp;argc, args, nullptr, nullptr) != napi_ok) {
        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "napi_init", "CreateNativeNode napi_get_cb_info failed");
    }
    if (argc != 1) {
        return nullptr;
    }
    // Get NodeContent handle from ArkTS side
    OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &amp;nodeContentHandle_);

    static ArkUI_NativeNodeAPI_1 *nodeAPI = nullptr;
    if (nodeAPI == nullptr) {
        nodeAPI = reinterpret_cast&lt;ArkUI_NativeNodeAPI_1 *&gt;(
            OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_NODE, "ArkUI_NativeNodeAPI_1"));
    }
    if (nodeAPI != nullptr && nodeAPI->createNode != nullptr && nodeAPI->addChild != nullptr) {
        ArkUI_NodeHandle DivComponent = CreateDivNodeHandle();
        OH_ArkUI_NodeContent_AddNode(nodeContentHandle_, DivComponent);
    }
    return returnVal;
}

static ArkUI_NodeHandle CreateDivNodeHandle() {
    ArkUI_NodeHandle greenDivNodeHandle = CreateDivNodeHandleWithParam(200, 0xFF00FF00);
    CAPIManager::GetInstance()->greenDivNodeHandle = greenDivNodeHandle;
    return greenDivNodeHandle;
}
</code>

The detailed attribute setting for the div node:

<code>static ArkUI_NodeHandle CreateDivNodeHandleWithParam(float height, uint32_t borderColor) {
    ArkUI_NodeHandle divNode = CAPIManager::getNodeAPI()->createNode(ARKUI_NODE_FLEX);
    // margin
    ArkUI_NumberValue number = {.f32 = 5};
    ArkUI_AttributeItem marginValue = {.value = &amp;number, .size = 1};
    // border width
    ArkUI_NumberValue number2 = {.f32 = 2};
    ArkUI_AttributeItem borderWValue = {.value = &amp;number2, .size = 1};
    // border color
    ArkUI_NumberValue number1 = {.u32 = borderColor};
    ArkUI_AttributeItem borderColorItem = {.value = &amp;number1, .size = 1};
    // width & height
    ArkUI_NumberValue number3 = {.f32 = height};
    ArkUI_AttributeItem hValue = {.value = &amp;number3, .size = 1};
    ArkUI_NumberValue number5 = {.f32 = 0.9};
    ArkUI_AttributeItem wValue = {.value = &amp;number5, .size = 1};
    // alignment
    ArkUI_NumberValue number4 = {.i32 = ARKUI_ITEM_ALIGNMENT_CENTER};
    ArkUI_AttributeItem alignment = {.value = &amp;number4, .size = 1};

    CAPIManager::getNodeAPI()->setAttribute(divNode, NODE_MARGIN, &amp;marginValue);
    CAPIManager::getNodeAPI()->setAttribute(divNode, NODE_BORDER_WIDTH, &amp;borderWValue);
    CAPIManager::getNodeAPI()->setAttribute(divNode, NODE_BORDER_COLOR, &amp;borderColorItem);
    CAPIManager::getNodeAPI()->setAttribute(divNode, NODE_WIDTH_PERCENT, &amp;wValue);
    CAPIManager::getNodeAPI()->setAttribute(divNode, NODE_HEIGHT, &amp;hValue);
    CAPIManager::getNodeAPI()->setAttribute(divNode, NODE_ALIGN_SELF, &amp;alignment);
    return divNode;
}
</code>

Although the creation process is complex, mastering these core steps allows developers to follow the documentation and implement native nodes.

3.2 Other Scenarios

Beyond the ArkTS‑to‑C example, developers can explore additional integration patterns in the official documentation.

cross-platformHarmonyOSUI performanceArkTSC interface
JD Cloud Developers
Written by

JD Cloud Developers

JD Cloud Developers (Developer of JD Technology) is a JD Technology Group platform offering technical sharing and communication for AI, cloud computing, IoT and related developers. It publishes JD product technical information, industry content, and tech event news. Embrace technology and partner with developers to envision the future.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.