ArkUI supports creating imperative nodes using the ArkTS language on the frontend, that is, FrameNodes. It also allows creating imperative nodes using the C language on the native side, and both types of nodes can be used together to build pages.
For this scenario, ArkUI provides cross-language attribute setting for imperative nodes, meaning nodes created with ArkTS can have their attributes set on the native side, and nodes created in C can have their attributes set in ArkTS.
In the examples below, native-side configuration must be completed first. For details, see Integrating with ArkTS Pages.
Cross-language refers to operations between ArkTS and C languages. Cross-language options refer to permission settings for cross-language operations on imperative nodes.
You can set a node's cross-language options using setCrossLanguageOptions and OH_ArkUI_NodeUtils_SetCrossLanguageOption. If the node cannot modify or set cross-language options, an exception will be thrown.
You can obtain the current cross-language options using getCrossLanguageOptions and OH_ArkUI_NodeUtils_GetCrossLanguageOption.
The following example demonstrates how to set and obtain cross-language options for ArkTS imperative nodes.
// Index.ets
import { NodeController, UIContext, FrameNode, typeNode, BuilderNode } from '@kit.ArkUI';
@Builder
function insideScroll() {
Column() {
ForEach([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], (item: number) => {
Text(item.toString())
.width("75%")
.height(50)
.backgroundColor(0xFFFFFF)
.borderRadius(15)
.fontSize(30)
.textAlign(TextAlign.Center)
.margin({ top: 10 })
}, (item: number) => item.toString())
}
.width("100%")
}
class MyNodeController extends NodeController {
uiContext: UIContext | null = null;
rootNode: FrameNode | null = null;
scrollNode: FrameNode | null = null;
scroller: Scroller = new Scroller();
makeNode(uiContext: UIContext): FrameNode | null {
this.uiContext = uiContext;
this.rootNode = new FrameNode(uiContext);
this.rootNode.commonAttribute.width("80%").height("50%").borderWidth(2).margin(15);
const scroll = typeNode.createNode(uiContext, 'Scroll');
scroll.initialize(this.scroller).id("scroll");
this.scrollNode = scroll;
this.rootNode.appendChild(this.scrollNode);
const builderNode = new BuilderNode(uiContext);
builderNode.build(wrapBuilder(insideScroll));
this.scrollNode?.appendChild(builderNode.getFrameNode());
return this.rootNode;
}
}
@Entry
@Component
struct CrossLanguage {
myNodeController: MyNodeController = new MyNodeController()
@State attributeSetting: boolean = false;
@State getCrossLanguageOptions: string = '{"attributeSetting": false}';
build() {
Scroll() {
Column({ space: 15 }) {
Column() {
Scroll() {
Column() {
NodeContainer(this.myNodeController)
Button("setCrossLanguageOptions").margin({ bottom: 15})
.onClick(() => {
this.attributeSetting = !this.attributeSetting;
this.myNodeController.scrollNode?.setCrossLanguageOptions({
attributeSetting: this.attributeSetting
});
// If attributeSetting is true, scrollNode supports attribute modification via non-ArkTS languages; otherwise, it does not.
this.getCrossLanguageOptions = JSON.stringify(this.myNodeController.scrollNode?.getCrossLanguageOptions());
})
Text("CrossLanguageOptions: " + this.getCrossLanguageOptions)
}
}.scrollBarColor(Color.Transparent)
}
.width('100%')
.height(350)
.backgroundColor(0xeeeeee)
.id('Part_TS')
}
.width('100%')
}.scrollBarColor(Color.Transparent)
}
} After obtaining a node, if its cross-language configuration allows attribute setting, ArkTS can use the getAttribute API to access objects for modifying native node attributes, while the native side can use setAttribute to modify ArkTS node attributes.
The following example creates an ArkTS Scroll node and modifies its properties on the native side.
Create an imperative node of type Scroll on the ArkTS side.
// Index.ets
import nativeNode from 'libentry.so';
import { NodeController, UIContext, FrameNode, typeNode, BuilderNode, NodeContent } from '@kit.ArkUI';
@Builder
function insideScroll() {
Column() {
ForEach([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], (item: number) => {
Text(item.toString())
.width("75%")
.height(50)
.backgroundColor(0xFFFFFF)
.borderRadius(15)
.fontSize(30)
.textAlign(TextAlign.Center)
.margin({ top: 10 })
}, (item: number) => item.toString())
}
.width("100%")
}
class MyNodeController extends NodeController {
uiContext: UIContext | null = null;
rootNode: FrameNode | null = null;
scrollNode: FrameNode | null = null;
scroller: Scroller = new Scroller();
makeNode(uiContext: UIContext): FrameNode | null {
this.uiContext = uiContext;
this.rootNode = new FrameNode(uiContext);
this.rootNode.commonAttribute.width("80%").height("50%").borderWidth(2).margin(15);
const scroll = typeNode.createNode(uiContext, 'Scroll');
scroll.initialize(this.scroller).id("scroll");
this.scrollNode = scroll;
this.rootNode.appendChild(this.scrollNode);
const builderNode = new BuilderNode(uiContext);
builderNode.build(wrapBuilder(insideScroll));
this.scrollNode?.appendChild(builderNode.getFrameNode());
return this.rootNode;
}
}
@Entry
@Component
struct CrossLanguage {
private myNodeController: MyNodeController = new MyNodeController();
@State attributeSetting: boolean = false;
@State getCrossLanguageOptions: string = '{"attributeSetting": false}';
private rootSlot = new NodeContent();
aboutToAppear(): void {
nativeNode.createNativeRoot(this.rootSlot);
}
build() {
Scroll() {
Column({ space: 15 }) {
Column() {
Scroll() {
Column() {
NodeContainer(this.myNodeController)
Button("setCrossLanguageOptions").margin({ bottom: 15})
.onClick(() => {
this.attributeSetting = !this.attributeSetting;
this.myNodeController.scrollNode?.setCrossLanguageOptions({
attributeSetting: this.attributeSetting
});
// If attributeSetting is true, scrollNode supports attribute modification via non-ArkTS languages; otherwise, it does not.
this.getCrossLanguageOptions = JSON.stringify(this.myNodeController.scrollNode?.getCrossLanguageOptions());
})
Text("CrossLanguageOptions: " + this.getCrossLanguageOptions)
}
}.scrollBarColor(Color.Transparent)
}
.width('100%')
.height(350)
.backgroundColor(0xeeeeee)
.id('Part_TS')
Column() {
ContentSlot(this.rootSlot)
}
.width(500)
.height(400)
.id('Part_C')
}
.width('100%')
}.scrollBarColor(Color.Transparent)
}
} Create CrossLanguageExample.h to obtain the target node (created in ArkTS) and set its properties.
// CrossLanguageExample.h
#ifndef MYAPPLICATION_CROSSLANGUAGEEXAMPLE_H
#define MYAPPLICATION_CROSSLANGUAGEEXAMPLE_H
#include "ArkUINode.h"
#include <hilog/log.h>
namespace NativeModule {
std::shared_ptr<ArkUIBaseNode> CreateCrossLanguageExample() {
auto nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI();
// Create the root Scroll node.
ArkUI_NodeHandle scroll = nodeAPI->createNode(ARKUI_NODE_SCROLL);
ArkUI_NumberValue length_value[] = {{.f32 = 480}};
ArkUI_AttributeItem length_item = {length_value, sizeof(length_value) / sizeof(ArkUI_NumberValue)};
nodeAPI->setAttribute(scroll, NODE_WIDTH, &length_item);
ArkUI_NumberValue length_value1[] = {{.f32 = 650}};
ArkUI_AttributeItem length_item1 = {length_value1, sizeof(length_value1) / sizeof(ArkUI_NumberValue)};
nodeAPI->setAttribute(scroll, NODE_HEIGHT, &length_item1);
ArkUI_AttributeItem scroll_id = {.string = "Scroll_CAPI"};
nodeAPI->setAttribute(scroll, NODE_ID, &scroll_id);
// Create a Column node.
ArkUI_NodeHandle column = nodeAPI->createNode(ARKUI_NODE_COLUMN);
ArkUI_NumberValue value[] = {480};
ArkUI_AttributeItem item = {value, sizeof(value) / sizeof(ArkUI_NumberValue)};
nodeAPI->setAttribute(column, NODE_WIDTH, &item);
ArkUI_NumberValue column_bc[] = {{.u32 = 0xFFF00BB}};
ArkUI_AttributeItem column_item = {column_bc, 1};
nodeAPI->setAttribute(column, NODE_BACKGROUND_COLOR, &column_item);
ArkUI_AttributeItem column_id = {.string = "Column_CAPI"};
nodeAPI->setAttribute(column, NODE_ID, &column_id);
// Create a Text node.
ArkUI_NodeHandle text0 = nodeAPI->createNode(ARKUI_NODE_TEXT);
ArkUI_NumberValue text_width[] = {300};
ArkUI_AttributeItem text_item0 = {text_width, sizeof(text_width) / sizeof(ArkUI_NumberValue)};
nodeAPI->setAttribute(text0, NODE_WIDTH, &text_item0);
ArkUI_NumberValue text_height[] = {50};
ArkUI_AttributeItem text_item1 = {text_height, sizeof(text_height) / sizeof(ArkUI_NumberValue)};
nodeAPI->setAttribute(text0, NODE_HEIGHT, &text_item1);
ArkUI_AttributeItem text_item = {.string = "C sets attributes of TypeScript-created node"};
nodeAPI->setAttribute(text0, NODE_TEXT_CONTENT, &text_item);
ArkUI_NumberValue margin[] = {10};
ArkUI_AttributeItem item_margin = {margin, sizeof(margin) / sizeof(ArkUI_NumberValue)};
nodeAPI->setAttribute(text0, NODE_MARGIN, &item_margin);
// Create a Row node.
ArkUI_NodeHandle row0 = nodeAPI->createNode(ARKUI_NODE_ROW);
ArkUI_NumberValue width_value[] = {{.f32=330}};
ArkUI_AttributeItem width_item = {width_value, sizeof(width_value) / sizeof(ArkUI_NumberValue)};
nodeAPI->setAttribute(row0, NODE_WIDTH, &width_item);
nodeAPI->setAttribute(row0, NODE_HEIGHT, &text_item1);
nodeAPI->setAttribute(row0, NODE_MARGIN, &item_margin);
// Create a Button node.
ArkUI_NodeHandle bt0 = nodeAPI->createNode(ARKUI_NODE_BUTTON);
ArkUI_NumberValue btn_width[] = {150};
ArkUI_AttributeItem btn_item0 = {btn_width, sizeof(btn_width) / sizeof(ArkUI_NumberValue)};
nodeAPI->setAttribute(bt0, NODE_WIDTH, &btn_item0);
nodeAPI->setAttribute(bt0, NODE_HEIGHT, &text_item1);
nodeAPI->setAttribute(bt0, NODE_MARGIN, &item_margin);
ArkUI_AttributeItem bt0_item = {.string = "scrollBarColor"};
nodeAPI->setAttribute(bt0, NODE_BUTTON_LABEL, &bt0_item);
nodeAPI->registerNodeEvent(bt0, NODE_ON_CLICK, 0, nullptr);
ArkUI_NodeHandle bt1 = nodeAPI->createNode(ARKUI_NODE_BUTTON);
nodeAPI->setAttribute(bt1, NODE_WIDTH, &btn_item0);
nodeAPI->setAttribute(bt1, NODE_HEIGHT, &text_item1);
nodeAPI->setAttribute(bt1, NODE_MARGIN, &item_margin);
ArkUI_AttributeItem bt1_item = {.string = "scrollBarWidth"};
nodeAPI->setAttribute(bt1, NODE_BUTTON_LABEL, &bt1_item);
nodeAPI->registerNodeEvent(bt1, NODE_ON_CLICK, 1, nullptr);
// Register events.
auto onClick = [](ArkUI_NodeEvent *event) {
ArkUI_NodeHandle node = OH_ArkUI_NodeEvent_GetNodeHandle(event);
auto nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI();
if (OH_ArkUI_NodeEvent_GetTargetId(event) == 0) { // scrollBarColor
ArkUI_NodeHandle node_ptr = nullptr;
OH_ArkUI_NodeUtils_GetAttachedNodeHandleById("scroll", &node_ptr);
try {
ArkUI_NumberValue scroll_color_value[] = {{.u32 = 0xff00ff00}};
ArkUI_AttributeItem scroll_color_item = {scroll_color_value, sizeof(scroll_color_value) / sizeof(ArkUI_NumberValue)};
nodeAPI->setAttribute(node_ptr, NODE_SCROLL_BAR_COLOR, &scroll_color_item);
} catch (...) {
OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "CrossLanguageExample", "crossLanguage setAttribute error");
}
}
if (OH_ArkUI_NodeEvent_GetTargetId(event) == 1) { // scrollBarWidth
ArkUI_NodeHandle node_ptr = nullptr;
OH_ArkUI_NodeUtils_GetAttachedNodeHandleById("scroll", &node_ptr);
try {
ArkUI_NumberValue scroll_width_value[] = {{20}};
ArkUI_AttributeItem scroll_width_item = {scroll_width_value, sizeof(scroll_width_value) / sizeof(ArkUI_NumberValue)};
nodeAPI->setAttribute(node_ptr, NODE_SCROLL_BAR_WIDTH, &scroll_width_item);
} catch (...) {
OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "CrossLanguageExample", "crossLanguage setAttribute error");
}
}
};
nodeAPI->registerNodeEventReceiver(onClick);
// Add nodes.
nodeAPI->addChild(scroll, column);
nodeAPI->addChild(column, text0);
nodeAPI->addChild(column, row0);
nodeAPI->addChild(row0, bt0);
nodeAPI->addChild(row0, bt1);
return std::make_shared<ArkUINode>(scroll);
}
} // namespace NativeModule
#endif // MYAPPLICATION_CROSSLANGUAGEEXAMPLE_H Mount the native node in NativeEntry.cpp.
// NativeEntry.cpp
#include <arkui/native_node_napi.h>
#include <js_native_api.h>
#include "NativeEntry.h"
#include "CrossLanguageExample.h"
namespace NativeModule {
napi_value CreateNativeRoot(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// Obtain NodeContent.
ArkUI_NodeContentHandle contentHandle;
OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle);
NativeEntry::GetInstance()->SetContentHandle(contentHandle);
// Create a node.
auto node = CreateCrossLanguageExample();
// Keep the native-side object in the management class to maintain its lifecycle.
NativeEntry::GetInstance()->SetRootNode(node);
return nullptr;
}
napi_value DestroyNativeRoot(napi_env env, napi_callback_info info) {
// Release the native-side object from the management class.
NativeEntry::GetInstance()->DisposeRootNode();
return nullptr;
}
} // namespace NativeModule Update the CMakeLists.txt file and add the required link libraries.
// CMakeLists.txt
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.5.0)
project(CAPI_DEMO)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
if(DEFINED PACKAGE_FIND_FILE)
include(${PACKAGE_FIND_FILE})
endif()
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
add_library(entry SHARED napi_init.cpp NativeEntry.cpp)
target_link_libraries(entry PUBLIC libace_napi.z.so libace_ndk.z.so hilog_ndk.z.so) Run the program. On the ArkTS side, click the button to set attributeSetting to true. On the native side, click the buttons to set the Scroll component's scrollbar color and width.

Only the following node types support cross-language attribute setting. The native side must use OH_ArkUI_NodeUtils_GetAttachedNodeHandleById to obtain the node whose attributes are to be modified. Cross-language attribute setting is not supported for target nodes obtained via OH_ArkUI_GetNodeHandleFromNapiValue.
Scroll, WaterFlow, Grid, and List nodes created through the C API support binding the scroll controller Scroller on the ArkTS side by using bindController. Starting from API version 26.0.0, calling the corresponding APIs of the four components above does not require enabling cross-language attribute setting.
| ArkTS TypedFrameNode type | Native ArkUI_NodeType type | ArkTS Attribute Getter | ArkTS Controller Getter/Binding |
|---|---|---|---|
| Button | ARKUI_NODE_BUTTON | getAttribute | NA |
| Checkbox | ARKUI_NODE_CHECKBOX | getAttribute | NA |
| Radio | ARKUI_NODE_RADIO | getAttribute | NA |
| Slider | ARKUI_NODE_SLIDER | getAttribute | NA |
| Toggle | ARKUI_NODE_TOGGLE | getAttribute | NA |
| Progress | ARKUI_NODE_PROGRESS | getAttribute | NA |
| LoadingProgress | ARKUI_NODE_LOADING_PROGRESS | getAttribute | NA |
| Image | ARKUI_NODE_IMAGE | getAttribute | NA |
| XComponent | ARKUI_NODE_XCOMPONENT | getAttribute | getController |
| Column | ARKUI_NODE_COLUMN | getAttribute | NA |
| Row | ARKUI_NODE_ROW | getAttribute | NA |
| Stack | ARKUI_NODE_STACK | getAttribute | NA |
| Flex | ARKUI_NODE_FLEX | getAttribute | NA |
| RelativeContainer | ARKUI_NODE_RELATIVE_CONTAINER | getAttribute | NA |
| Swiper | ARKUI_NODE_SWIPER | getAttribute | bindController |
| Scroll | ARKUI_NODE_SCROLL | getAttribute | bindController |
| List | ARKUI_NODE_LIST | getAttribute | bindController |
| ListItem | ARKUI_NODE_LIST_ITEM | getAttribute | NA |
| ListItemGroup | ARKUI_NODE_LIST_ITEM_GROUP | getAttribute | NA |
| WaterFlow | ARKUI_NODE_WATER_FLOW | getAttribute | bindController |
| FlowItem | ARKUI_NODE_FLOW_ITEM | getAttribute | NA |
| Grid | ARKUI_NODE_GRID | getAttribute | bindController |
| GridItem | ARKUI_NODE_GRID_ITEM | getAttribute | NA |
| Text | ARKUI_NODE_TEXT | getAttribute | bindController |
| TextInput | ARKUI_NODE_TEXT_INPUT | getAttribute | bindController |
| TextArea | ARKUI_NODE_TEXT_AREA | getAttribute | bindController |