Ensure that the Lynx Debug and Lynx DevTool switches are enabled in the Switch page of Lynx Explorer.
### Run Lynx DevTool Desktop Application
:::tip Get Lynx DevTool Desktop Application
You can visit [Lynx DevTool](https://github.com/lynx-family/lynx-devtool/releases) to get the latest version of Lynx DevTool desktop application.
:::
Launch Lynx DevTool Desktop Application.
### Use Data Cable to Connect Debugging Device
Use a data cable to connect the debugging device, and Lynx DevTool Desktop Application will automatically recognize the debugging device and display the Lynx app information on the debugging device.
## View Device Connection Status
In the toolbar, you can view the connection status of the current device.
- USB represents that the device is connected via USB cable.
- Time represents the delay of communication with the device.
## Choose Debugging Device
In the toolbar, you can click this button, and select other connected devices from the pop-up menu.
:::tip Tip
After switching devices, please ensure that the Lynx app on the device is running in the foreground.
:::
## Common Issues and Troubleshooting
**Question**: Why can't Lynx DevTool desktop application recognize my debugging device?
**Answer**: Make sure you have connected your development device (e.g., your MacBook) and the device running the Lynx application (e.g., your phone) using a data cable.
For iOS devices, ensure that you have installed Xcode and iOS SDK with matching versions on your development device.
For Android devices, in addition to ensuring a data cable connection, you also need to enable Developer Mode and USB debugging on your Android device:
1. Enable Developer Mode on your Android device - specific steps may vary between devices, please refer to the [Android Developer Documentation](https://developer.android.com/studio/debug/dev-options)
2. Enable "USB Debugging" in the developer options
3. When the device is connected to your computer, an authorization prompt will appear, click "Allow"
You can try launching Xcode or Android Studio to compile and run an application to verify that you can connect to the device properly.
## Compatibility
**Error:** No compatibility data found for `devtool.integration.connection`
## Provide Feedback
Welcome to experience Lynx DevTool. If you need a hand, please file an issue in [Lynx Issues](https://github.com/lynx-family/lynx-devtool/issues). Thank you!
---
url: /guide/devtool/handle-errors.md
---
# Handle Errors in Lynx
## Recognize Errors in Tools
When you are developing Lynx apps, you may encounter various types of errors that are designed to notify you the unexpected situations that have just happened.
For example, if the bundle file is broken, you'll see:
Also, sometimes when your JavaScript code contains an TypeError:
Sometimes, a [native module](/guide/spec.md#nativemodules) is called with a wrong number of arguments:
With DevTool or LogBox, you shall easily recognize them.
## LogBox
You've already seen it above - the in-app tool that displays errors.
There may be multiple Lynx page instances at the same time in a page.
For debugging between different page instances,
you need to select the current page instance to be debugged.
At the bottom of the debugging options page, you can select the page to be debugged.
Move the mouse over the page path, and the page thumbnail will be displayed on the right.
In the debugging options menu, there are App Info for displaying application and Lynx information,
and Settings for setting debugging options.
## Debugging Panels
Dive into the debugging functions.
### Elements

The **Elements** panel allows you to inspect and modify elements.
- [View Element Nodes](/guide/devtool/panels/elements-panel.md#view-dom-nodes)
- [Edit the Element](/guide/devtool/panels/elements-panel.md#edit-the-dom)
- [View and Change CSS](/guide/devtool/panels/elements-panel.md#view-and-change-css)
- [Find Invalid, Overridden, and Other CSS](/guide/devtool/panels/elements-panel.md#find-invalid-overridden-and-other-css)
### Console

Use the **Console** panel to view logged messages and run JavaScript.
- [View Logged messages](/guide/devtool/panels/console-panel.md#view-logged-messages)
- [Run JavaScript](/guide/devtool/panels/console-panel.md#run-javascript)
- [Clear the Console](/guide/devtool/panels/console-panel.md#clear-the-console)
### Sources

Use the **Sources** panel to debug JavaScript.
- [File Navigation](/guide/devtool/panels/sources-panel.md#file-navigation)
- [Pause Code with Breakpoints](/guide/devtool/panels/sources-panel.md#pause-code-with-breakpoints)
- [Debug JavaScript](/guide/devtool/panels/sources-panel.md#debug-javascript)
- [Debug Original Code with Source Maps](/guide/devtool/panels/sources-panel.md#debug-original-code-with-source-maps)
- [Debug the Main Thread](/guide/devtool/panels/sources-panel.md#debug-the-main-thread)
### Layers

Helps you understand the composition of Lynx pages and how the framework presents content, analyzing its 3D layers to discover rendering issues.
- [View page Layers](/guide/devtool/panels/layers-panel.md#view-page-layers)
- [Inspect page Layers](/guide/devtool/panels/layers-panel.md#inspect-page-layers)
---
url: /guide/devtool/panels/console-panel.md
---
# Console Panel
## Overview
Use the **Console** panel to [view logged messages](#view-logged-messages) and [run JavaScript](#run-javascript). Before you start debugging, please take some time to familiarize yourself with the [Lynx JavaScript Runtime](/guide/scripting-runtime/index.md#javascript-runtime).
### Open the Console Panel
The Console can be opened as a panel or as a tab in the Drawer.
#### Open in the Drawer
If you want to view the Console panel while using other panels, you can open the Console in the Drawer.
Please refer to [Open the Console in the Drawer | Chrome DevTools](https://developer.chrome.com/docs/devtools/console/reference#drawer).
### Console Settings
The following links explain each setting:
- [Selected context only](#filter-messages-from-different-contexts)
- [Group similar messages in console](#disable-message-grouping)
- [Eager evaluation](#disable-eager-evaluation)
- [Autocomplete from history](#disable-autocomplete-from-history)
### Console Sidebar
Please refer to [Open the Console Sidebar | Chrome DevTools](https://developer.chrome.com/docs/devtools/console/reference#sidebar).
## View Logged messages
The types of logs currently can be viewed include:
1. **JavaScript logs**. Including the main thread and the background thread.
- By default, logs from the background thread are displayed in full format, while logs from the main thread are serialized as strings with the `[main-thread.js]` prefix.
- When [Main Thread Debugging](/guide/devtool/panels/sources-panel.md#debug-the-main-thread) is enabled, logs from the main thread will also be displayed in full format.
2. **Some client logs**.
Currently, client runtime errors and some other client logs are serialized as strings and displayed in the Console panel.
### Log Sources
For JavaScript logs, the `App.tsx:11` on the right side of the log represents where it logged. Clicking it will open the [Sources](/guide/devtool/panels/sources-panel.md) panel and highlight the line of code that caused the message to get logged to the Console.
### Disable Message Grouping
DevTool enables **Group similar messages in console** by default, which aggregates similar messages logged consecutively.
Open [Console Settings](#console-settings) and disable this option to expand the logs that were originally grouped.
### View Stack Traces
### Filter messages
#### Filter by Log Level
Please refer to [Filter by log level | Chrome DevTools](https://developer.chrome.com/docs/devtools/console/reference#level).
:::tip
When the sidebar is open, you cannot click the log level drop-down.
:::
#### Filter by Text
Please refer to [Filter by text | Chrome DevTools](https://developer.chrome.com/docs/devtools/console/log#text).
#### Filter by Regular Expressions
Please refer to [Filter by regular expression | Chrome DevTools](https://developer.chrome.com/docs/devtools/console/log#regex).
#### Filter by URL
Please refer to [Filter messages by URL | Chrome DevTools](https://developer.chrome.com/docs/devtools/console/reference#url).
#### Filter Messages from Different [Contexts](/guide/spec.md#scripting-runtime-enviroment①)
By default, all logs are displayed within the context of the background thread. When [Main thread debugging](/guide/devtool/panels/sources-panel.md#debug-the-main-thread) is enabled, an additional context of the main thread will be added.
As shown in the figure, `Background:-1` represents the background thread, and `Main` represents the main thread.
When you [run JavaScript in the Console](#run-javascript), it will execute only within the currently selected context.
Open [Console Settings](#console-settings) and enable **Selected context only** checkbox to display logs only from the currently selected context.
### Search for Text
Please refer to [Search for text in logs | Chrome DevTools](https://developer.chrome.com/docs/devtools/console/reference#search).
## Run JavaScript
The Console is a **REPL**, which stands for "Read, Evaluate, Print, and Loop." It reads the JavaScript you enter, evaluates your code, outputs the result of the expression, and then loops back to the first step.
You can enter expressions related to the current page in the Console, such as `this`.
You can also enter expressions unrelated to the current page, such as `1+2`.
Press Enter to get the result, and the Console will output the result of the expression below the code.
### String Copy Options
Please refer to [String copy options | Chrome DevTools](https://developer.chrome.com/docs/devtools/console/reference#string-copy-options).
For example, in this case, the results of copying are as follows:
```javascript
{"a":123,"b":"string"}
'{"a":123,"b":"string"}'
"{\"a\":123,\"b\":\"string\"}"
```
### Re-run Expressions from History
Please refer to [Re-run expressions from history | Chrome DevTools](https://developer.chrome.com/docs/devtools/console/reference#history).
### Watch Expression Values in Real-Time
Please refer to [Watch JavaScript values in real time with Live Expressions | Chrome DevTools](https://developer.chrome.com/docs/devtools/console/live-expressions).
### Disable Eager Evaluation
Please refer to [Disable Eager Evaluation | Chrome DevTools](https://developer.chrome.com/docs/devtools/console/reference#eagereval).
### Disable Autocomplete from History
Please refer to [Disable autocomplete from history | Chrome DevTools](https://developer.chrome.com/docs/devtools/console/reference#autocomplete).
## Clear the Console
You can use any of the following workflows to clear the Console:
-
## Compatibility
**Error:** No compatibility data found for `devtool.panels.console`
---
url: /guide/devtool/panels/elements-panel.md
---
# Elements Panel
The **Elements** panel allows you to inspect and modify element and element tree.
## Overview
The **Elements** panel provides a powerful interface for inspecting and manipulating the element and the element tree. You can select specific element using the element tree and use other tools to make modifications.
The **Elements** panel also includes tabs for the following related tools:
- **Styles**:
- [View and debug](#viewing-and-changing-css) CSS rules applied to an element from all stylesheets.
- Find any [invalid, overridden, or otherwise not working CSS](#invalid-and-declarations-with-invalid-values).
- Modify elements by [adding declarations](#adding-css-declarations-to-an-element), and [interacting with the Box model](#interacting-with-elements-in-the-lynx-page-preview-window-via-the-box-model).
- **Computed**: Lists the resolved properties applied to an element.
### Open the Elements Panel
By default, when you open the Lynx DevTool desktop application and connect to a device to debug Lynx pages, the **Elements** panel will open.
To manually open or switch back to the **Elements** panel from another panel, click on the **Elements** tab in the main panel of the DevTool window.
### Preview Panel
The left side of the **Elements** panel is the Lynx Page Preview Window, showing the Lynx page content for mobile devices in real-time.
#### Introduction to the Preview Panel
As shown in the above figure:
1. Click the reload icon in the upper left corner to refresh the Lynx page.
2. SD/HD toggle switch, adjust current debugging screen mirroring clarity.
3. Switch current screen mirroring mode, there are two modes: LynxView and FullScreen.
- When there are multiple Lynx pages on the current screen, it is recommended to use the LynxView screen mirroring mode, so that you can focus on the currently selected debugging page.
- If the Lynx page has an overlay component (this component is in a separate window and may be outside the LynxView display area), it is recommended to use the FullScreen mode, and the entire screen content of the mobile phone will be captured at this time.
4. The border that separates the Elements panel and the preview window can be dragged to adjust their size.
5. Displays the JS engine used by the current page.
#### Open/Close the Preview Panel
Click on this icon to open/close the preview window.
After closing, it will be displayed as follows:
At this time, if an element that can generate UI is selected in the Elements panel (not all nodes on the element tree will eventually generate platform UI), the Lynx page on the mobile phone will highlight the corresponding element.
## View Element Nodes
### Inspect Nodes
1. Click on the **Inspect** icon in the upper left corner of the developer tools.
2. Click on the logo in the upper left corner of the preview window.
3. Now, the corresponding `
### Navigate the Element Tree with a keyboard
After selecting a node in the element tree, you can use the keyboard to browse the element tree.
1. After clicking on the **Inspect** icon in the upper left corner of the developer tools, click on the input box in the middle of the Lynx page preview window.
Now, the `` node is selected in the element tree.
2. Press the Up arrow key twice. `
3. Press the left arrow key. All child nodes of `
2. Press the right arrow key on the keyboard to expand the `
6. Press the Enter key, and you can see that the text change has taken effect both in the element tree and on the left preview window.
### Edit Attributes
To modify attributes, double-click on the attribute name or value. Follow the instructions below to learn how to modify the style attribute of a node.
1. Select the `
2. Double-click on the part that displays `style="height:100px;"`, and the text will be highlighted.
3. Modify the **style** content to `style="width:70%;height:100px;"`.
4. Press the Enter key, and you can see that the width change of the view has taken effect both in the element tree and on the left preview window.
## View and Change CSS
### View CSS for an Element
1. Click on the **Inspect** icon, and click on the text **Lynx Explorer** in the preview window.
2. The `
4. Switch to the **Computed** tab in the upper tab bar, and you can see the box model of the selected node, as well as all the resolved styles finally applied to the node.
### Add CSS Declarations to an Element
If you want to change or add CSS declarations to an element, use the **Styles** tab.
1. Selected `
4. Press the Enter key again. At this time, whether it is the UI effect on the preview window or the element tree, the style change has taken effect.
{' '}
### Interact with Elements in the Lynx Page preview window via the Box Model
1. Click on the **Inspect** icon, and hover over the Go button in the preview window.
2. At this time, you can see that all the box models are highlighted in the left preview window, from the inside to the outside are **content-box**/ **padding-box** / **border-box** / **margin-box**.
3. Click Go button, then corresponding `
## Find Invalid, Overridden, and Other CSS
### Check the CSS You Wrote
Suppose you added some CSS to an element and want to make sure the new styles are applied correctly. When you refresh the page, the element looks the same as before. Something went wrong.
The first thing to do is to inspect the element and make sure the new CSS is actually applied to the element.
Sometimes, you will see the new CSS in the **Elements** > **Styles** pane, but the new CSS appears as faded text, cannot be modified, is crossed out, or a warning or hint icon is displayed next to it.
### Understand CSS in the **Styles** Pane
The Styles pane can identify various CSS issues and highlight them in different ways.
#### Invalid and Declarations with Invalid Values
The **Styles** pane will cross out the following and display a warning icon next to the following:
- When the CSS property is invalid or unknown, the entire CSS declaration (property and value).
- When the CSS property is valid but the value is invalid, the entire CSS declaration (property and value).
#### Overridden
The **Styles** pane will cross out properties that are overridden by other properties according to the cascade order.
In this example, the `color:red;` style property on the element will override the `color:linear-gradient(120deg,#0095ff 30%,#42d392 100%);` on the `.banner.title` class.
#### Inherited and Non-Inherited
The **Styles** pane will list properties in the `Inherited from
## Compatibility
### View and Edit
**Error:** No compatibility data found for `devtool.panels.elements.view-and-edit`
### Preview
**Error:** No compatibility data found for `devtool.panels.elements.preview`
---
url: /guide/devtool/panels/layers-panel.md
---
# Layers Panel
The **Layers** panel helps you understand the composition of Lynx pages and how the framework presents content, analyzing its 3D layers to discover rendering issues.
## Overview
Use the **Layers** panel to perform the following operations:
- View page layers
- Inspect page layers
### Open the Layers Panel
To open the Layers panel, follow these steps:
1. Press **Command+Shift+P** to open the command menu.
2. Start typing **Layers**, select **Show Layers**, and then press **Enter**.
Or, select the More options in the upper right corner -> **More tools** -> **Layers**.
## View page Layers
The leftmost part of the **Layers** panel lists all the rendered layers of the Lynx page in an expandable tree. This tree structure is updated as the mobile **Lynx** page is updated. Layers are identified by their **CSS** selector or a number (followed by the layer size in pixels).
Hover the mouse over a layer to highlight it on the Lynx page preview window. A tooltip will be displayed on the page preview, which contains the following information:
- The selector of the layer
- The size of the layer (in pixels)
## Inspect page Layers
Click on a layer to view more information in the **Details** pane.
Depending on the layer, the following information will be displayed:
- Size
- Compositing reason
- Memory estimate
- Number of draws
The following figure shows the stacking and arrangement of the layers of this **Lynx** page.
To move the chart, do the following:
- Use **WASD** to move the chart. Press W to pan up, A to pan left, S to pan down, and D to pan right.
- Click the **Pan Mode** icon to move along the X and Y axes.
- Click the **Rotate Mode** icon to rotate along the Z axis.
- Click **Reset Transform** to reset the chart to its original position.
- Scroll the mouse wheel up to zoom in.
- Scroll the mouse wheel down to zoom out.
To view the element corresponding to the layer in the **Elements** panel, right-click on the corresponding layer in the chart or layer tree, and then click **Reveal in Elements panel**.
## Compatibility
**Compatibility Table**
**Query:** `devtool.panels.layers`
**Platform Support**
| Platform | Version Added | Notes |
|----------|---------------|-------|
| Android | 3.0 | - |
| iOS | 3.0 | - |
| HarmonyOS | 3.0 | - |
**Description:** View page layers
---
url: /guide/devtool/panels/preact-devtools-panel.md
---
# Preact Devtools Panel
The **Preact Devtools** panel helps you inspect the hierarchy of ReactLynx components, displaying information such as component props, state, and file paths. The Preact Devtools panel is based on the [Preact Devtools](https://preactjs.github.io/preact-devtools/) web browser extension and offers the same user experience.
## Overview
With the **Preact Devtools** panel, you can:
- View the component hierarchy of ReactLynx
- Modify props or state and see real-time updates
- Use ClickToComponent to navigate to component source code
- Use the Profiler to analyze application performance
### Open the Preact Devtools Panel
In the DevTool window, click the Preact Devtools tab in the main panel to open it.
Note: Before using the **Preact Devtools** panel, you need to import [`@lynx-js/preact-devtools`](https://www.npmjs.com/package/@lynx-js/preact-devtools) at the **first line** of your ReactLynx application's entry file:
```js
import '@lynx-js/preact-devtools';
```
## View Component Hierarchy
The left side of the **Preact Devtools** panel shows the complete hierarchy of the ReactLynx application, while the right side displays detailed information about the selected component, including its props, state, and file path.
Hover over a component to highlight its corresponding box model in the preview window on the left.
## View and Modify Props or State
On the right side of the **Preact Devtools** panel, you can directly modify the props or state of components, and the ReactLynx application's UI will update in real-time.
As shown below, we changed the state used for counting in the `Counter` component from `1` to `2`, and the counter UI on the page also changed from `1` to `2`.
## ClickToComponent for Source Navigation
We integrated the [ClickToComponent](https://github.com/ericclemmons/click-to-component) into the **Preact Devtools** panel. By clicking the file path under **source location**, VSCode will automatically open the file containing the component and navigate to the corresponding line and column.
## Profiler
The Profiler panel supports performance analysis of ReactLynx component rendering time on background thread. Click the button in the top-left corner to start recording, let the application run for a while, and then click the button again to stop. You will then get information about all Preact commits during that time, as well as the rendering time of each component within the same commit.
---
url: /guide/devtool/panels/sources-panel.md
---
# Sources Panel
Use the **Sources** panel to debug JavaScript. Before you start debugging, please take some time to familiarize yourself with the [Lynx JavaScript Runtime](/guide/scripting-runtime/index.md#javascript-runtime).
After DevTool is enabled, the background thread uses the PrimJS engine for debugging by default. On Android, you can also switch to the V8 engine for a more comprehensive debugging experience.
To switch to the V8 engine, open the [DevTool Switch Page](/guide/start/integrate-lynx-devtool-advanced.md#debugging-devtool-switch), toggle the "**V8 Engine**" switch to "**On**" and restart the application. You can check the current engine type in the lower-left corner of the [Preview Window](/guide/devtool/panels/elements-panel.md#introduction-to-the-preview-panel).
## Overview
The **Sources** panel has three sections:
1. **File Navigation Pane**. All JavaScript files of the page are listed here.
2. **Code Editor Pane**. After selecting a file in the **File Navigation Pane**, the contents of the file are displayed here.
3. **Debugger Pane**. Various tools for inspecting the page's JavaScript.
## File Navigation
### Open File
You can use the **File Navigation Pane** or the **Open file** feature to open the file of interest.
You can enter the file URL here or select a file from the drop-down list.
The bottom status bar of the **Code Editor Pane** will display the line and column number of the current mouse position.
### Close File
You can close a file in the following ways:
- Hover the mouse over the file name tab at the top of the **Code Editor Pane** and click the **close** button.
- Right-click the file name tab at the top of the **Code Editor Pane**:
- Close: Close the current file.
- Close others: Close other files.
- Close all: Close all files.
### Locate File
To locate the file currently displayed in the **Code Editor Pane**:
1. Right-click anywhere in the **Code Editor Pane** or right-click the file name tab at the top of the **Code Editor Pane**.
2. Select **Reveal in sidebar**.
### Search Code
To search a code segment:
1.
2. Enter text and press Enter to search.
3. Click search results to jump to the corresponding file and the code will be highlighted.
You can also:
-
- Click the breakpoint icon again to delete the breakpoint.
- In the **Breakpoints** pane, click the checkbox next to the breakpoint entry to enable or disable the breakpoint. When disabled, the marker next to the line number will become transparent.
- Right-click the breakpoint icon to see the options menu:
- Remove breakpoint.
- Edit breakpoint.
- Disable/Enable breakpoint.
- Right-click the breakpoint entry in the **Breakpoints** pane to see the options menu:
- Remove breakpoint.
- Reveal location.
- Deactivate/Activate breakpoints.
#### Skip Breakpoints with 'Never Pause Here'
Hover your mouse over the position shown in the image, then enable **First-Line Breakpoints**. This is a non-persistent global switch that takes effect for all pages during a single APP run. The switch state will be reset after closing and restarting the APP.
You can debug first-line breakpoints in two ways:
1. Turn on the switch, then open the page you want to debug.
2. Open the page you want to debug, turn on the switch, and then [reload](/guide/devtool/panels/elements-panel.md#introduction-to-the-preview-panel) the page.
### Exception Breakpoints
Use exception breakpoints when you want to pause on the line of code that's throwing a caught or uncaught exception.
1. In the **Breakpoints** pane, click the **Pause on exceptions** button, which turns blue when enabled.
2. By default, it only pause on uncaught exceptions. If you also want to pause on caught exceptions, check the **Pause On Caught Exceptions** checkbox.
:::warning
Currently, the PrimJS engine does not distinguish between caught and uncaught
exceptions, and all exceptions will cause a pause. In contrast, the V8 engine
distinguishes between caught and uncaught exceptions.
:::
## Debug JavaScript
By default, you can only debug the background thread. If you need to debug the main thread, please refer to the section [Debug the Main Thread](#debug-the-main-thread).
### Step Through Code
Once your code is paused, step through it, one expression at a time, investigating control flow and property values along the way.
#### Step Over
Please refer to [Step over line of code | Chrome DevTools](https://developer.chrome.com/docs/devtools/javascript/reference#step-over).
#### Step Into
Please refer to [Step into line of code | Chrome DevTools](https://developer.chrome.com/docs/devtools/javascript/reference#step-into).
#### Step Out
Please refer to [Step out of line of code | Chrome DevTools](https://developer.chrome.com/docs/devtools/javascript/reference#step-out).
#### Run All Code Up to a Certain Line
For example, in this case, select **Terminate script execution**, DevTool will
stop executing the script, the rest of the code in `add` will not be executed,
and you will see that the value of `count` will not change.
### View and Edit Properties
Please refer to [View and edit local, closure, and global properties | Chrome DevTools](https://developer.chrome.com/docs/devtools/javascript/reference#scope).
### View the Current Call Stack
When paused on a line of code, you can use the **Call Stack** pane to view the call stack that got you to this point.
Select an entry in the pane to jump to the line of code where the function was called. A blue arrow icon represents the currently highlighted code.
#### Copy Stack Trace
Right-click anywhere in the **Call Stack** pane and select **Copy stack trace** to copy the current call stack to the clipboard.
For example, in this case, the copied stack trace is as follows:
```
add (App.tsx:11)
publishEvent (tt.js:148)
```
### Ignore Scripts
#### Unignore Scripts
To unignore scripts, follow the same steps as above and select **Remove from ignore list**.
You can also unignore scripts through the prompt at the top of the **Code Editor Pane**.
### Watch the Values of Custom JavaScript Expressions
Please refer to [Watch the values of custom JavaScript expressions | Chrome DevTools](https://developer.chrome.com/docs/devtools/javascript/reference#watch).
### Inspect and Edit Scripts
In the **Code Editor Pane**, you can browse and edit code.
#### Make Minified Files Readable
Click the `{}` button in the bottom status bar of the editor, and the **Sources** panel will present the minified file in a more readable format to improve readability.
#### Edit Scripts
If loading fails, the **Console** logs an error similar to the following.
When you open any compiled file, DevTool will notify you if it found the source map.
#### Load a Source Map Manually
You can manually load a source map:
1. Open the compiled file, right-click anywhere in the **Code Editor Pane**, and select **Add source map**.
2. Enter the source map URL in the textbox, then click **Add**.
### Debug with Source Maps
1. In the original file, you can [set breakpoints](#pause-code-with-breakpoints) as you normally would, or you can set breakpoints in the compiled file, and DevTool will automatically jump to the original file.
2. After triggering a breakpoint and pausing, the **Call Stack** pane will display the name of the original file.
3. The bottom status bar of the **Code Editor Pane** will show a link to the compiled file it points to. Click it to jump to the corresponding file.
## Debug the Main Thread
Debugging the main thread is similar to debugging the background thread, but it requires some additional steps to enable.
### Preparation
To debug the main thread, you need to start the project in **dev** mode using rspeedy.
You can check the following two items to ensure preparation is done:
1. A `debug-info.json` file is generated in the [intermediate output directory](/api/rspeedy/rspeedy.distpath.intermediate.md).
2. In the `tasm.json` file of the [intermediate output directory](/api/rspeedy/rspeedy.distpath.intermediate.md), the `templateDebugUrl` field is a valid URL pointing to the `debug-info.json` file mentioned in step one. Refer to [rspeedy.dev.assetprefix](/api/rspeedy/rspeedy.dev.assetprefix.md).
### Enable Main Thread Debugging
Hover over the position shown in the image below, then enable **Main Thread Debugging**. This is a non-persistent global switch that takes effect for all pages during a single APP run. The switch state will be reset after closing and restarting the APP.
To enable main thread debugging:
1. Turn on the switch, then open the page you want to debug. Or open the page you want to debug, turn on the switch, and then [reload](/guide/devtool/panels/elements-panel.md#introduction-to-the-preview-panel) the page.
2. The `main-thread.js` file of the main thread will be displayed in the **File Navigator Pane** (formerly known as `lepus.js`).
3. In the **Threads** pane, switch to **Main** to start debugging.
In the **Threads** pane, you can switch between debugging the main thread or the background thread. The blue arrow icon represents which context is selected. The currently paused thread will be marked as **paused**.
## Compatibility
### Breakpoints
**Error:** No compatibility data found for `devtool.panels.sources.breakpoints`
### JS Engine
**Error:** No compatibility data found for `devtool.panels.sources.JSEngine`
---
url: /guide/devtool/recorder.md
---
# Recorder
Recorder is a tool designed for the Lynx framework to facilitate the recording and replay of page runtime states. Its core functionality involves precisely capturing the complete state of a page at a specific moment and serializing it into a portable recording file for subsequent high-fidelity replay.
## What it does?
Unlike traditional screen recording technologies, Recorder achieves "Deterministic Replay." During the replay process, the system not only reconstructs the complete UI hierarchy and rendering data from the time of recording but also preserves all page interactivity. Crucially, it achieves temporal consistency by hijacking and mocking time-related APIs, ensuring that time-sensitive operations within the replayed environment yield the same results as they did during the original session.
Recorder features cross-device replay capabilities. Through its integrated Replayer decoder, a recording file can be parsed and replayed on any device that supports the Lynx framework, regardless of hardware model or operating system. For instance, a session recorded on an iPad can be accurately reproduced on an iPhone, an Android device, or even on desktop and TV applications built with the same Lynx technology stack.
## What can you use it for?
As an application's feature set expands, Lynx pages often become tightly coupled with the native application environment. This includes dependencies on custom NativeModules or private cloud services for data and resource management.
When diagnosing complex issues, this strong coupling becomes a major impediment to external collaboration and debugging, leading to problems such as build failures, complex authentication procedures, and inaccessible backend services. In such scenarios, traditional methods like code review are inefficient, while source-level debugging is often impractical due to environmental discrepancies.
A core design objective of Recorder is to decouple the page from its private environment. A recording file encapsulates all information required for the page to run, including the view hierarchy, data state, and responses from asynchronous requests. During replay, all external dependencies are simulated using the data contained within the recording file, eliminating the need for any live network requests. This makes the debugging process entirely independent of the original production environment.
Technical Advantages:
- Environment-Agnostic: Debuggers do not need to compile the original application, configure complex authentication, or set up specific development environments.
- Self-Contained Context: A single recording file contains all the necessary context to reproduce an issue.
- Efficient Collaboration: The party reporting the issue simply provides a recording file, allowing the recipient to debug in a standardized replay environment. This significantly reduces communication overhead and the difficulty of issue reproduction.
## Try Recorder
Recorder's record and replay capabilities are already integrated by default in LynxExplorer. You can follow the steps below to experience them.
2. Enter the Recorder plugin dashboard and click the Start button to activate.
3. Operate on your phone, navigate to the target page you want to record and interact with it until it reaches the desired state.
:::note
It is especially important to note that, in order to ensure the completeness of the recorded page data, please make sure to click Start **before** navigating to the target page.
:::
Here is a sample page that you can access by scanning the QR code or entering the link in LynxExplorer.
### Page Replay
1. Artifact Hosting
The Lynx DevTool Application only provides local file hosting for each recorded artifact. If you need to share the record artifact or access it across devices, you will need to host the artifact separately.
2. Replay using LynxExplorer
Here is a sample recorded artifact that you can replay by scanning the QR code or entering the link in LynxExplorer.
### 2. Tracing Engine Execution
Trace dives deep into the [Engine](/guide/spec.md#engine) and thoroughly records:
- The sequence of task scheduling and queue switching
- The full picture of event handling chains
- Precise timing and relationships for API calls
This information is useful for analyzing issues like asynchronous task congestion or event response delays.
### 3. Rich Performance Analysis Tools
Trace is a multidimensional performance analysis platform:
- **[Element](/guide/spec.md#element) Analysis**: Track changes in the Element tree structure, identify high-frequency or heavy node operations, and uncover performance risks in your page structure.
- **[NativeModules](/guide/spec.md#native-module) Call Analysis**: Monitor detailed NativeModules call processes, clearly display the timing and parameters of each NativeModule call, and pinpoint potential bottlenecks.
- **Smoothness Analysis**: Use flame charts of task durations to quickly locate the root causes of jank, helping you optimize animations and interactions.
## Next Steps
### Start recording
Click `Start` button to begin recording trace.
### Stop recording
When finished, click `Stop` button to end the recording.
### Decode JavaScript Profile Data using sourcemap (Optional)
If your scripts is [minified](/api/rspeedy/rspeedy.minify.md), you can use [sourcemap](/api/rspeedy/rspeedy.sourcemap.md) to decode profile data.
### Set duration (seconds)
Enter the desired duration for recording trace during app startup. Press the `Enter` key to save your configuration.
### Restart application
Close and relaunch your app. Trace will automatically record trace during the startup period.
### Load startup trace
After the app launches, choose target app, then click the `Load Trace` button in Lynx DevTool.
### View the startup trace
Once the trace data has loaded, you will see a visualization of the app’s startup process for detailed analysis.
### Select the app
Pick the target app you want to profile from the available device list.
### Start recording
Press the `Start` button to start recording trace.
### Stop recording
Click the `Stop` button to stop recording trace.
### Analyze
After stopping, the trace data will be displayed for analysis.
### Download trace data(optional)
If needed, click the `Download` button to save the trace file to your computer.
### View or manage historical trace data
Click **History** button to see previous trace data.
You can select a trace file to `view`, `download`, or `delete` as needed on Trace History panel.
The Trace UI consists of the following key components:{' '}
- Topbar: Includes a search box, SourceMap Decode, Focus LynxView, and other features.
- Timeline: Displays the occurrence and duration of TraceEvents. Supports adding annotations (notes), measuring intervals, and more.
- TrackTreeView: Organizes TraceEvents by track (typically one or more per thread), making it easy to distinguish between different threads/tasks.
- DetailPanel: Shows detailed information about a selected TraceEvent, including parameters, duration, thread, process, and more.
## Common Operations
### Open a Trace File
- Drag and drop a trace file directly onto the page for automatic parsing and display of trace.
### Zoom and Pan the Timeline
- Zoom in/out: Use the `w` (zoom in) and `s` (zoom out) keys.
- Pan left/right: Use the `a` (left) and `d` (right) keys.
### Search for TraceEvents
- Click on the Timeline to add a note at the corresponding position. Notes can be named for easier identification and navigation.
### Measure Time Intervals
- Click and drag on the Timeline to measure the interval between two points. This is useful for precisely analyzing the duration of tasks or operations.
### Focusing LynxView
Trace data may contain events from multiple Lynx pages. Trace supports focusing on one or more specific Lynx pages, making it easier to locate and analyze performance issues for particular pages.
#### 2. Select the Lynx page(s) by URL
After selecting the Lynx page(s), Trace events from unselected pages will be grayed out.
### Querying the System Where the Trace Was Recorded
1. Enter a colon in the Topbar search box to enter SQL query mode;
2. Input the following SQL statement to query the system information where the Trace was recorded:
```sql
select name, str_value from metadata where name like "system%";
```
## Additional Resources
For a more detailed guide on using the Trace UI and its advanced features, please refer to the official Perfetto documentation: [perfetto-ui](https://perfetto.dev/docs/visualization/perfetto-ui)
---
url: /guide/embed-lynx-to-native.md
---
# Embedding LynxView into Native View
[LynxView](/guide/spec.md#lynxview) itself is a native view that can easily be used as a full-screen view, or be [embedded](/guide/spec.md#embedded) within a non-full-screen native view.
LynxView corresponds to the [Page](/api/elements/built-in/page.md) element. It only allows the client to set size constraints on LynxView, and you generally cannot directly modify the Page's style to set its size.
The client sets different size constraints on LynxView, which translates to setting size constraints on the Page.
The Lynx [layout engine](/guide/spec.md#starlight) uses these constraints to calculate the size of the Page node and all its child nodes.
## Constrain the size of LynxView \{#Constraining-LynxView}
### Constraint mode
As shown in the figure, developers can choose to handle the event in the main thread or the background thread.
- When timely event response is not required, you can choose to handle the event in the background thread, and the event processing of the background thread will not block the main thread.
- When timely event response is required, you can choose to handle the event in the main thread, which can avoid event delays caused by cross-threading, but it should be noted that excessive event processing may cause the main thread to be busy.
Specifically, if developers want to listen to the click event of a certain node, they can set the event handler property of type `bind` on the node:
```tsx
## Monitor exposure of the entire page
When developers need to monitor exposure/anti-exposure events of nodes in the entire page, they can subscribe to the exposure event [`exposure`](/api/lynx-api/event/global-event.md#exposure) and anti-exposure event [`disexposure`](/api/lynx-api/event/global-event.md#disexposure) of the node with the [`exposure-id`](/api/elements/built-in/view.md#exposure-id) attribute set through [`GlobalEventEmitter`](/guide/interaction/event-handling/event-propagation.md#globaleventemitter).
In the following example, the developer uses [`GlobalEventEmitter`](/guide/interaction/event-handling/event-propagation.md#globaleventemitter) to monitor whether the node in `ComponentA` is exposed, and outputs the exposed node [`exposure-id`](/api/elements/built-in/view.md#exposure-id) when it is exposed.
**Example 1:**
**This is an example below: event**
**Entry:** `src/visibility_expose_global`
**Bundle:** `dist/visibility_expose_global.lynx.bundle` | Web: `dist/visibility_expose_global.web.bundle`
```tsx {8-12,14-21,56}
import { root, useState } from "@lynx-js/react";
import { useLynxGlobalEventListener } from "@lynx-js/react";
export function ComponentA() {
const [eventLog, setEventLog] = useState
### Locating dropped-frame segments during scrolling
- On the timeline, look for frames that clearly exceed the cadence (for example, consecutive red labels) and mark them as “long-frame segments.”
- Analyze frame by frame to confirm whether the cause is UI-thread congestion, delayed render submission, or resource loading/decoding inserted into the critical path.
- Associate long-frame segments with UI elements (large images, complex list items, card animations) to form actionable ties to what users see.
### Root-cause patterns and identification
- UI thread blocking: layout and measurement stacking, or I/O running on the UI thread.
- Heavy layout and drawing: deep hierarchies, frequent reflow, or large-area repaint; concentrated text and image size computation; complex `Canvas` drawing. Symptom: a single frame’s computation time is unusually long and repeats with scroll.
- Image and resource loading: decode, downsample, and texture upload blocking critical frames; async not truly isolated, or missing placeholders/preload. Manifestations: `GPU` or `RenderThread` queues back up, correlated with image size or count.
### Common investigation suggestions
- Reduce UI-thread workload: move non-essential computation and I/O off the UI thread; spread layout and drawing across frames to avoid “everything in a single frame.”
- Optimize layout depth and measurement: control hierarchy depth and constraints complexity; avoid repeated measurement; use compositing layers to reduce unnecessary repaint.
- Image and resource governance: constrain dimensions and size, use placeholders and preload; move decode and texture upload earlier or run in parallel; load on demand and reuse caches.
- JS and bridge: split long tasks and reduce synchronous calls; use throttling and batching to avoid high-frequency messages overwhelming the UI cadence.
---
url: /guide/performance/analysis-performance/analysis-memory.md
---
# Memory Usage Analysis
Memory analysis helps discover leaks and abnormal usage within Lynx pages and ensures stability. You can use Trace Memory Track and IDE tools, to comprehensively monitor and troubleshoot memory-related performance issues.
## Deep analysis with Trace
Starting from 3.4, Trace supports Memory Track, which shows Lynx page memory usage trends over time. Each Memory Track represents one Lynx page’s memory changes.
Click the Memory Track curve to see the page’s total memory usage and the memory used by Element, background scripting engine, main-thread scripting engine, images, etc.
## Analyze memory with IDEs
This section briefly introduces how to use Android Studio or Xcode’s built-in tools to debug apps and view performance metrics.
Choose Leaks, then specify the device and app to debug. In this example, an iPhone SE2 (iOS 14.5) simulator debugs the LynxExample app. Click the start button to check for memory leaks.
After starting, operate in the simulator or on device. If the Leaks window shows issues, jump to the corresponding code to debug. You can pause to inspect memory info shown in the chart. After debugging, you can save the results locally.
If a memory leak appears (example below), set the chart to Call Tree mode, then enable Invert Call Tree at the bottom of the window. Select any leak to jump to source code.
### Allocations (detailed allocation)
Xcode > Open Developer Tool > Instruments > Allocations
Similar to Leaks but focuses on detailed memory allocations. The lower list shows per-method memory usage. Check a graph to show a bar chart above. Click an item to view stack trace details; click the right-hand Stack Trace pane to jump into code and inspect allocations.
The code section is highlighted and the allocation ratio is shown.
###
First click the highlighted area in the toolbar; Android Studio performs a Build and then opens the Profiler window. From there you can run tests—click the ‘+’ icon to select a device connected via adb and the process to test.
Using a Xiaomi MIX (API 24), we run a \~45 s test and manually trigger a GC at 15 s. From left to right, the icons are GC, Capture Heap Dump, and manual Record (an option for devices below API 26 for memory analysis).
Over 45 s, the device’s memory changes are as follows:
In this example, after the manual GC, the app’s memory usage drops noticeably for a period.
Heap dumps show which objects are using memory at the time of capture. Especially after long user sessions, heap dumps reveal objects that should no longer be in memory, helping identify leaks. After clicking the Heap Dump icon, Java memory may temporarily increase because dumping runs in the same process and needs memory to collect data.
After capturing the heap dump, use the built-in analyzer. The table shows:
- Allocations: number of allocations in the heap.
- Native Size: total native memory used by this object type (Bytes). Visible on Android 7.0+ only. Some Java-allocated framework classNames (e.g., Bitmap) consume native memory.
- Shallow Size: total Java memory used by this object type (Bytes).
- Retained Size: total memory retained by all instances of this className (Bytes).
Click a className in the chart to open the Instance List window, then jump to source for inspection.
Or use the Reference window for more detailed checks and jump elsewhere for inspection.
After capturing a heap dump, you can also export hprof data from the SESSIONS window via Export Heap Dump. Use other tools (such as MAT) to further analyze the heap.
### Memory profiler
On devices below API 26, you need to manually record a time window before running memory performance analysis. Use the filter options (red circle) such as Arrange by className. In the table, select a className to jump to the Instance View, then to Allocation Call Stack to view stack traces and jump to source.
### Symptoms of memory leaks
- Many Full GCs during runtime.
- During a specific thread’s execution, manual GC has little effect.
- Many allocations and frees in a short time (memory graph jitter).
### Example leak and related analysis
```java
final className LoadedInChildClassLoader {
static final byte[] moreBytesToLeak = new byte[1024 * 1024 * 100];
private static final ThreadLocal
After the test, you can save data as a table and view performance parameters such as Jank count. Use the tool’s metrics as simple references to check app performance.
---
url: /guide/performance/analysis-performance/analysis-native-module.md
---
# NativeModule Invocation
NativeModule is the core communication bridge between JavaScript and native applications. By invoking a NativeModule, Lynx frontend pages can access and control native capabilities. This document together with code examples, details the invocation and execution flow of NativeModule within Trace.
## Deep analysis with Trace
Since Lynx 3.2, the Trace page includes a NativeModule Track that aggregates all NativeModule invocations. Each item’s length reflects the overall duration from request initiation to completion.
Basic display: click an item in the NativeModule Track to view a color-coded breakdown of rough durations for each stage of the invocation.
Details panel: shows the method name, input parameters, and precise per-stage durations (including parameter conversion, platform logic, thread switches, and other key nodes).
## Breakdown of NativeModule invocation details
Below we use a frontend NativeModule call as an example to describe execution timing in Trace.
### Example code
```ts {4,5,6,7,10}
NativeModules.bridge.call(
'setStorage',
{
data: {
key: 'lynx_nativemodule_test',
value: i,
},
},
(res) => {
console.log('setStorage res is: ', res);
},
);
```
### Trace execution analysis
A NativeModule call can be divided into five main stages:
#### 1. Parameter conversion
This stage corresponds to `JSValueToPubValue` in Trace, where JS data (lines 4–7 in the code sample) is converted into native types.
#### 2. Platform-layer implementation
This stage corresponds to the time period in the Trace from the start of `CallPlatformImplementation` to `NativeModule::PlatformCallbackStart`, where the native method executes the specific functionality and returns platform-layer data.
Among them, the `CallPlatformImplemention` occurs on the [background thread](/guide/spec.md#background-scripting-thread-historically-known-as-js-thread), while the logic between the `CallPlatformImplementation` event and `NativeModule::PlatformCallbackStart` usually runs on other threads.
#### 3. Waiting for the background thread to execute the callback
This stage corresponds to `NativeModule::PlatformCallbackStart` and `NativeModule::Callback` in Trace.
#### 4. Result conversion
This stage corresponds to `PubValueToJSValue` in Trace, where platform-layer return data is converted into JS arguments.
#### 5. Callback execution
This stage corresponds to `InvokeCallback` in Trace, which executes JS code logic (line 10 in the sample).
## Special NativeModule calls
Currently, JSB SDKs in the company return results via callbacks. Some NativeModule implementations return results directly to the Lynx SDK via callbacks on the background thread. Callback execution is not necessarily limited to the main thread—any thread may invoke and execute callback-related logic.
### Example code
```js {4,5,6,9}
NativeModules.bridge.call(
'x.reportAppLog',
{
data: {
eventName: 'lynx_nativemodule_test_event_name',
},
},
(res) => {
console.log('reportAppLog res is: ' + JSON.stringify(res));
},
);
```
### Trace execution analysis
These special NativeModule calls can be divided into five main stages.
#### 1. Parameter conversion
This stage corresponds to `JSValueToPubValue` in Trace, where JS data (lines 4–6 in the sample) is converted into native types.
#### 2. Platform-layer implementation
This stage corresponds to the time period in the Trace from `CallPlatformImplementation` to the start of `NativeModule::Callback`, where the native method executes the specific functionality and returns platform-layer data.
#### 3. Result conversion
This stage corresponds to `PubValueToJSValue` in Trace, where platform-layer return data is converted into JS arguments.
#### 4. Callback execution
This stage corresponds to `InvokeCallback` in Trace, which executes JS code logic (line 9 in the sample).
#### 5. Cleanup
This stage corresponds to the time period in the Trace from the end of `InvokeCallback` to the end of `NativeModule::Invoke`, where platform-layer cleanup is performed and external registrants are notified that the NativeModule call has completed.
---
url: /guide/performance/analysis-performance/analysis-render-process.md
---
# Render Time Analysis
This document aims to help developers master how to accurately locate each execution stage in a Trace, thereby enabling effective analysis of specific performance issues.
## Analyze render time with Trace
Frontend pages can [flag Pipeline](/guide/performance/monitor-performance/timing-flag.md) for key components. When the component finishes rendering on screen, the [Timing Track](/guide/performance/analysis-performance/trace-track.md) in Trace generates a bubble at the corresponding timestamp to mark the key stage.
A Lynx page generally has two render types: first-frame rendering and update rendering. Internally, the Lynx SDK produces [LoadBundleEntry](/api/lynx-api/performance-api/performance-entry/load-bundle-entry.md) and [PipelineEntry](/api/lynx-api/performance-api/performance-entry/pipeline-entry.md) to describe stage durations in each process.
Click a bubble to open a details panel showing the sub-stage timeline and the duration of each sub-stage in the render process.
The following sections detail both render types and their constituent stages, including the corresponding Trace event names, so you can locate and troubleshoot issues via Trace events.
## First-frame render time analysis
First-frame rendering primarily consists of LoadBundle and Paint:
### LoadBundle
This stage corresponds to `Timing::Mark.loadBundleStart` and `Timing::Mark.loadBundleEnd` in Trace. It mainly parses the bundle and performs the initial screen render. It can be broken down into six sub-stages:
#### Parse
This stage corresponds to `Timing::Mark.parseStart` and `Timing::Mark.parseEnd` in Trace. It parses the binary bundle into in-memory data structures. The bundle contains a header and multiple sections; the JS source section runs on the [background thread](/guide/spec.md#background-thread-aka-off-main-thread), mapped to `Timing::Mark.loadBackgroundStart` and `Timing::Mark.loadBackgroundEnd` in Trace.
#### MTS Render
This stage corresponds to `Timing::Mark.mtsRenderStart` and `Timing::Mark.mtsRenderEnd` in Trace. It executes [Main Thread Script (MTS)](/guide/spec.md#main-thread-script-or-mts) to build the Element Tree.
#### Resolve
This stage corresponds to `Timing::Mark.resolveStart` and `Timing::Mark.resolveEnd` in Trace. It traverses the element tree, computes a layout node tree from node attributes and styles, and generates [UI Paint OP](/guide/spec.md#platform-ui-paint-operations-or-ui-paint-op).
#### Layout
This stage corresponds to `Timing::Mark.layoutStart` and `Timing::Mark.layoutEnd` in Trace. It traverses the layout node tree produced during Resolve, computes node positions and sizes, and generates [UI Layout OP](/guide/spec.md#platform-ui-layout-operations-or-ui-layout-op).
#### Paint UI OP Execute
This stage corresponds to `Timing::Mark.paintingUiOperationExecuteStart` and `Timing::Mark.paintingUiOperationExecuteEnd` in Trace. It executes the UI Paint OP generated during Resolve.
#### Layout UI OP Execute
This stage corresponds to `Timing::Mark.layoutUiOperationExecuteStart` and `Timing::Mark.layoutUiOperationExecuteEnd` in Trace. It executes the UI Layout OP generated during Layout.
### Paint
The system drawing stage measures, lays out, and paints the platform UI tree. The end of painting maps to `Timing::Mark.paintEnd` in Trace.
## Update render time analysis
Update renders are primarily driven by the background thread to update and draw the UI tree. The main difference from first-frame rendering lies in [Framework Rendering](/guide/spec.md#framework-rendering). Using ReactLynx 3 as an example, we highlight the differing parts:
### Diff Changes
This stage corresponds to `Timing::MarkFrameWorkTiming.diffVdomStart` and `Timing::MarkFrameWorkTiming.diffVdomEnd` in Trace. It performs component diff on the background thread.
### Pack Changes
This stage corresponds to `Timing::MarkFrameWorkTiming.packChangesStart` and `Timing::MarkFrameWorkTiming.packChangesEnd` in Trace. It serializes diff results and sends them to the [main thread](/guide/spec.md#main-thread-or-lynx-main-thread).
### Parse Changes
This stage corresponds to `Timing::MarkFrameWorkTiming.parseChangesStart` and `Timing::MarkFrameWorkTiming.parseChangesEnd` in Trace. It parses the serialized data on the main thread.
### Patch Changes
This stage corresponds to `Timing::MarkFrameWorkTiming.patchChangesStart` and `Timing::MarkFrameWorkTiming.patchChangesEnd` in Trace. It applies the diff results on the main thread and updates the Element Tree.
Subsequent render stages are similar to the first-frame process: Resolve, Layout, Paint UI OP Execute, Layout UI OP Execute, and Paint.
## Next Steps
## First Screen Rendering
[First Screen Rendering](/guide/spec.md#first-screen-rendering-or-fsr) includes stages such as downloading the [Bundle](/guide/spec.md#lynx-bundle-or-bundle), loading the bundle, and drawing.
### Download Bundle
After the page starts, it first [downloads](/guide/spec.md#load) the Bundle。
### Load Bundle
Once the bundle is downloaded, it enters the loading stage.
#### Parse Bundle
During the bundle loading stage, the content of the bundle—such as CSS stylesheets, scripts, and page configurations—is first [parsed](/guide/spec.md#parse)
#### Execute MTS
After parsing the bundle, the [Engine thread](/guide/spec.md#engine-thread-historically-known-as-tasm-thread) virtual machine executes the [MTS](/guide/spec.md#main-thread-script-or-mts).
#### Execute BTS
Meanwhile, the [background thread](/guide/spec.md#background-scripting-thread-historically-known-as-js-thread) virtual machine loads, parses, and executes the [BTS](/guide/spec.md#background-thread-script-or-bts).
During BTS execution, you can observe the creation process of components.
:::tip
The background thread does not block the Engine thread’s Element tree construction, resolve, layout, and other flows.
:::
#### Construct the Element Tree
During the [Element tree](/guide/spec.md#element-tree) construction stage, the [Element PAPI](/guide/spec.md#element-papi) is called to convert the page structure into an Element tree.
Taking the [hello-world](https://github.com/lynx-family/lynx-examples/blob/main/examples/hello-world/) project as an example, the first frame creates 1 page element, 6 view elements, 2 image elements, and 5 text elements.
```html
#### Resolve
During the [Resolve](/guide/spec.md#resolve) stage, the Element’s className, inline styles, and other attributes are parsed to determine the Element’s style, and the [layout node tree](/guide/spec.md#layout-node-tree) is created.
As shown below, the Resolve stage covers the complete execution flow and the parsing of styles for `
#### Layout
The [layout](/guide/spec.md#layout) stage measures and lays out the layout nodes. The following example shows the layout process of the `
#### Flush
After the layout is complete, [platform UIs](/guide/spec.md#platform-ui) are created, with various attributes and layout information set.
:::tip
Element nodes containing only layout attributes will not create platform UI nodes.
[Nested text](/api/elements/built-in/text.md#嵌套text) will be merged with their parent text node to create a single platform UI.
::
In the [hello-world](https://github.com/lynx-family/lynx-examples/blob/main/examples/hello-world/) project, the following nodes create platform UI on the first screen:
```html
## Update Rendering Process
Taking the [hello-world](https://github.com/lynx-family/lynx-examples/blob/main/examples/hello-world/) project as an example, clicking the Logo triggers an update, which demonstrates the component update rendering flow.
### Trigger the Click Event
After clicking the Logo, LynxView processes the [click event](/guide/spec.md#event-propagation) and sends the event to the background thread, triggering the corresponding node’s click [event handler](/guide/spec.md#event-handler).
To better observe the execution timing of the click event callback in Trace, you can use the [Trace API](/guide/performance/analysis-performance/trace-api.md) to add custom Trace events.
```javascript {3,5}
const onTap = useCallback(() => {
'background only';
lynx.performance.profileStart('onTap');
setAlterLogo(!alterLogo);
lynx.performance.profileEnd();
}, [alterLogo]);
```
The App component updates the state of alterLogo in the click event callback.
### Diff Component Diff
After updating the component state, the component diff process is triggered, and the image node changes:
```html
### Component Update Synchronization
After the component diff is complete, the update information is synchronized to the Engine thread, driving subsequent UI changes. In Trace, clicking the `CallLepusMethod` event allows you to see the UI update process after component diff is complete.
### UI Update
The Engine thread shows the update process of the Element tree. As shown below, the Element tree removes one element and adds a new image element.
After the Element update is complete, the layout and UI are updated again.
### Trigger useEffect Callback
After the Element tree update is complete, the background thread is notified to trigger the component’s useEffect callback.
To better observe the execution timing of the useEffect callback in Trace, use the [Trace API](/guide/performance/analysis-performance/trace-api.md) to add custom trace events.
```javascript {6-8}
useEffect(() => {
console.info('Hello, ReactLynx');
}, []);
useEffect(() => {
lynx.performance.profileMark('useEffect', {
args: { alterLogo: `${alterLogo}` },
});
}, [alterLogo]);
```
---
url: /guide/performance/analysis-performance/trace-api.md
---
# Using Trace API
Trace allows you to add custom trace events to your code, helping you track specific operations or logic flows. This is useful for profiling custom business logic, measuring durations, or marking important points in your app.
- For Frontend Developers: You might want to measure the execution timing of a hook or component lifecycle method to understand rendering delays or side effect durations. For example, tracking how long a `useEffect` hook takes;
- For Android/iOS Developers: You may want to profile how long it takes to load a Lynx Bundle, parse resources, or execute a specific NativeModule call. Custom trace events help you pinpoint slow operations within complex workflows;
By adding custom trace events, you transform opaque code sections into visible, measurable segments in Trace’s timeline, enabling precise performance tuning.
## How to Use
- Definition: Slice events have both a start and end timestamp, representing a duration.
- Nesting: On the same thread, slice events can be nested like a call stack.
- For example, if event B starts after event A but before A ends, B is considered a child of A and will be displayed nested under A in the Trace UI.
- Important: Child events must always end before their parent ends (i.e., B must end before A).
- Use case: Suitable for profiling code sections where execution time matters.
```objc {3,5,10,12}
// Basic usage
- (void)measure {
[LynxTraceEvent beginSection:@"render" withName:@"measure"]; // 'measure' slice start
// ... your code ...
[LynxTraceEvent endSection:@"render" withName:@"measure"]; // 'measure' slice end
}
// With custom arguments
- (void)draw {
[LynxTraceEvent beginSection:@"render" withName:@"draw-image" debugInfo:@{@"component": @"Image", @"size": @"large"}];
// ... your code ...
[LynxTraceEvent endSection:@"render" withName:@"draw-image"];
}
```
### Instant Events
- Definition: Instant events have only a single timestamp and no duration. - Use
case: Suitable for marking significant moments or points in your code (such as
state changes, cross-thread/async boundaries, etc.).
```objc {4,11}
// Basic usage
- (void)requestBegin {
// ...
[LynxTraceEvent instant:@"network" withName:@"request-begin"];
// ...
}
// With custom arguments
- (void)requestFinished {
// ...
[LynxTraceEvent instant:@"network" withName:@"request-finished" debugInfo:@{@"url": @"https://example.com", @"method": @"GET"}];
// ...
}
```
## Best Practices
### Begin/End Must Match on the Same Thread
- Every `beginSection` must have a corresponding `endSection`, and both calls must occur on the same thread.
- Do not let exceptions or early returns prevent `endSection` from being called.
#### Bad Examples
```objc {6-7,13-14,19-20,23}
// Exception causes endSection not to be called
- (void)measureWithError:(BOOL)shouldThrow {
[LynxTraceEvent beginSection:@"measure"];
// ...
@throw [NSException exceptionWithName:@"TestException" reason:@"Error occurred" userInfo:nil];
// endSection will not be called due to exception
[LynxTraceEvent endSection:@"measure"];
}
// Early return causes endSection not to be called
- (void)measureWithFastExit:(BOOL)fastExit {
[LynxTraceEvent beginSection:@"measure"];
// Early return, endSection not called
if (fastExit) return;
// ...
[LynxTraceEvent endSection:@"measure"];
}
// Cross-thread begin/end mismatch
[LynxTraceEvent beginSection:@"background-task"];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// ...
[LynxTraceEvent endSection:@"background-task"]; // Error: not same thread
});
```
#### Good Examples
```objc {7-10,16-18,25-26,28}
- (void)measureWithError:(BOOL)shouldThrow {
@try {
[LynxTraceEvent beginSection:@"measure"];
// ...
@throw [NSException exceptionWithName:@"TestException" reason:@"Error occurred" userInfo:nil];
}
@finally {
// Exception safe: ensure endSection is always called
[LynxTraceEvent endSection:@"measure"];
}
}
- (void)measureWithFastExit:(BOOL)fastExit {
[LynxTraceEvent beginSection:@"measure"];
if (fastExit) {
// Early return safe
[LynxTraceEvent endSection:@"measure"];
return;
}
// ...
[LynxTraceEvent endSection:@"measure"];
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Thread safe: begin/end on the same thread
[LynxTraceEvent beginSection:@"background-task"];
// ...
[LynxTraceEvent endSection:@"background-task"];
});
```
### Do Not Use Slice Events Across Async Boundaries
- Do not use `beginSection`/`endSection` across asynchronous boundaries like timers or callbacks.
- Slice events require start and end to be in the same synchronous context.
- Use Instant events if you need to trace both sides of an async boundary.
#### Bad Examples
```objc {2,5,9,11}
// Timer/callback
[LynxTraceEvent beginSection:@"async-function"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, time), dispatch_get_main_queue(), ^{
// ...
[LynxTraceEvent endSection:@"async-function"];
});
// Async task
[LynxTraceEvent beginSection:@"await-task"];
[someAsyncFunction waitUntilFinished];
[LynxTraceEvent endSection:@"await-task"];
```
#### Good Examples
```objc {3,5,9,11}
// Timer/callback: use begin/end inside callback
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, time), dispatch_get_main_queue(), ^{
[LynxTraceEvent beginSection:@"async-function"];
// ...
[LynxTraceEvent endSection:@"async-function"];
});
// Async task: use instant events
[LynxTraceEvent instant:@"async-task" withName:@"start"];
[someAsyncFunction waitUntilFinished];
[LynxTraceEvent instant:@"async-task" withName:@"end"];
```
- Definition: Slice events have both a start and end timestamp, representing a duration.
- Nesting: On the same thread, slice events can be nested like a call stack.
- For example, if event B starts after event A but before A ends, B is considered a child of A and will be displayed nested under A in the Trace UI.
- Important: Child events must always end before their parent ends (i.e., B must end before A).
- Use case: Suitable for profiling code sections where execution time matters.
```java {3,5,10-13,15}
// Basic usage
void measure() {
TraceEvent.beginSection("render", "measure");
// ... your code ...
TraceEvent.endSection("render", "measure");
}
// With custom arguments
void draw() {
Map
- Definition: Instant events have only a single timestamp and no duration.
- Use case: Suitable for marking significant moments or points in your code (such as state changes, cross-thread/async boundaries, etc.).
```java {4,11-14}
// Basic usage
void requestBegin() {
// ...
TraceEvent.instant("network", "request-begin");
// ...
}
// With custom arguments
void requestFinished() {
// ...
Map
- Definition: Slice events have both a start and end timestamp, representing a duration.
- Nesting: On the same thread, slice events can be nested like a call stack.
- For example, if event B starts after event A but before A ends, B is considered a child of A and will be displayed nested under A in the Trace UI.
- Important: Child events must always end before their parent ends (i.e., B must end before A).
- Use case: Suitable for profiling code sections where execution time matters.
```javascript {3,5,10-14,16}
// Basic usage
function measure() {
TraceEvent.beginSection(TraceCategory.Other, "measure");
// ... your code ...
TraceEvent.endSection(TraceCategory.Other, "measure");
}
// With custom arguments
function draw() {
const args: Record
- Definition: Instant events have only a single timestamp and no duration.
- Use case: Suitable for marking significant moments or points in your code (such as state changes, cross-thread/async boundaries, etc.).
```javascript {4,11-15}
// Basic usage
function requestBegin() {
// ...
TraceEvent.instant(TraceCategory.Other, "request-begin")
// ...
}
// With custom arguments
function requestFinished() {
// ...
const args: Record
- Definition: Slice events have both a start and end timestamp, representing a duration.
- Nesting: On the same thread, slice events can be nested like a call stack.
- For example, if event B starts after event A but before A ends, B is considered a child of A and will be displayed nested under A in the Trace UI.
- Important: Child events must always end before their parent ends (i.e., B must end before A).
- Use case: Suitable for profiling code sections where execution time matters.
```javascript {3,5,10-12,14}
// Basic usage
function handleClick() {
lynx.performance.profileStart('handle-click');
// ... your code ...
lynx.performance.profileEnd();
}
// With custom arguments
useEffect(() => {
lynx.performance.profileStart('useEffect', {
args: { count },
});
// ... your code ...
lynx.performance.profileEnd();
}, [count]);
```
### Instant Events
- Definition: Instant events have only a single timestamp and no duration.
- Use case: Suitable for marking significant moments or points in your code (such as state changes, cross-thread/async boundaries, etc.).
```javascript {3,6-8}
function fetchData() {
// Basic usage
lynx.performance.profileMark('fetch-data-begin');
fetch(url).then((res) => {
// With custom arguments
lynx.performance.profileMark('fetch-data-end', {
args: { url: 'https://example.com', method: 'GET' },
});
});
}
```
#### Flow Events
- Description: Flows are used to link two (or more) logically related events (Slice or Instant) that may occur on different threads or at different times.
- Visualization: In the Trace UI, flows are displayed as arrows connecting related events. When you select one event, the arrow highlights its related events.
- Use case: Flows are especially useful for tracking the lifecycle of asynchronous tasks, request/response pairs, or any operation spanning multiple phases or contexts.
```javascript {1-2,7}
const flowId = lynx.performance.profileFlowId();
lynx.performance.profileMark('user-action-begin', { flowId });
// ...later, in an async callback
setTimeout(() => {
// ...
lynx.performance.profileMark('user-action-end', { flowId });
}, 1000);
```
## Best Practices
### Begin/End Must Match on the Same Thread
- Every `profileStart` must have a corresponding `profileEnd`, and both calls must occur on the same thread.
- Do not let exceptions or early returns prevent `profileEnd` from being called.
#### Bad Examples
```javascript {5-6,11-12,17-18,21}
function measure() {
lynx.performance.profileStart('measure');
// ...
throw new Error('Error occurred');
// profileEnd not called due to exception
lynx.performance.profileEnd();
}
function measureWithFastExit(fastExit) {
lynx.performance.profileStart('measure');
// Early return causes profileEnd not to be called
if (fastExit) return;
// ...
lynx.performance.profileEnd();
}
// Cross-async/thread begin/end mismatch
lynx.performance.profileStart('background-task');
setTimeout(() => {
// ...
lynx.performance.profileEnd();
}, 1000);
```
#### Good Examples
```javascript {6-9,15-17,24-25,27}
function measure(shouldThrow) {
try {
lynx.performance.profileStart('measure');
// ...
throw new Error('Error occurred');
} finally {
// Exception safe: ensure profileEnd is always called
lynx.performance.profileEnd();
}
}
function measureWithFastExit(fastExit) {
lynx.performance.profileStart('measure');
if (fastExit) {
// Early return safe
lynx.performance.profileEnd();
return;
}
// ...
lynx.performance.profileEnd();
}
setTimeout(() => {
// Thread safe: begin/end in the same execution context
lynx.performance.profileStart('background-task');
// ...
lynx.performance.profileEnd();
}, 0);
```
### Do Not Use Slice Events Across Async Boundaries
- Do not use `profileStart`/`profileEnd` across asynchronous boundaries such as timers, callbacks, or `await`/`Promise`.
- Slice events require start and end to be in the same synchronous context.
- Use Instant events if you need to trace both sides of an async boundary.
#### Incorrect Examples
```javascript {2,5,9,11}
// Timer/callback
lynx.performance.profileStart('async function');
setTimeout(() => {
// ...
lynx.performance.profileEnd();
}, 3000);
// await/promise
lynx.performance.profileStart('await-task');
await someAsyncFunc();
lynx.performance.profileEnd();
```
#### Correct Examples
```javascript {3,5,9,11}
// Timer/callback: use begin/end inside the callback
setTimeout(() => {
lynx.performance.profileStart('async function');
// ...
lynx.performance.profileEnd();
}, 3000);
// await/promise: use instant trace events
lynx.performance.profileMark('async-task:start');
await someAsyncFunc();
lynx.performance.profileMark('async-task:end');
```
## Common Tracks
### Process Track
The Process Track is typically named using the process name and process id (iOS and macOS Traces use `Process {pid}`). In Lynx Trace, there is usually only one Process Track, serving as the top-level container for all other Tracks.
### Thread Track
Thread Tracks are typically named using the thread name and thread id. Lynx Trace includes multiple thread Tracks such as the [UI thread](/guide/spec.md#ui-thread), [Engine thread](/guide/spec.md#engine-thread-historically-known-as-tasm-thread), and [background thread](/guide/spec.md#background-scripting-thread-historically-known-as-js-thread). Below are common Thread Tracks that help developers see the order, duration, and thread distribution of events to analyze behavior and bottlenecks.
#### UI Thread Track
The UI Thread Track shows all events and tasks on the application’s UI thread, such as page rendering, UI operations, and the main loop. Analyzing the UI Thread Track helps locate performance issues like UI stutter and render blocking. In Lynx Trace, a process contains a single UI Thread Track.
By default, the top-most Thread Track is the UI Thread Track.
- On Android and Harmony, the UI Thread Track is named `process-name pid(main thread)`.
- On iOS and macOS, the Thread Track with the smallest thread id is the UI Thread Track.
#### Background Thread Track
The Background Thread Track shows [BTS](/guide/spec.md#background-thread-script-or-bts) execution. Use this Track to analyze BTS scheduling, execution, and duration.
:::info
For historical reasons, the background thread is named Lynx\_JS.
:::
#### AsyncCreateView Track
The AsyncCreateView Track records the process of creating UI asynchronously and is suitable for analyzing asynchronous platform UI creation.
#### Event Report Track
The Event Report Track shows Lynx’s internal event reporting process, helping locate issues like whether an event was reported.
### Performance Issue Track
The Performance Issue Track is used to mark performance anomalies and bottlenecks. Developers can quickly locate hot spots and abnormal behavior via this Track.
### Timing Track
The Timing Track shows key timestamps (such as first screen render done and actual first meaningful paint), helping developers locate key milestones.
### NativeModule Track
The [NativeModule](/guide/use-native-modules.md#原生模块) Track shows the NativeModule invocation process, including JS–Native communication and data conversion. Use it to analyze native module flows and performance bottlenecks.
### CPU Frequency Track
The CPU Frequency Track displays changes in CPU frequency over time as a line chart, helping developers analyze CPU resource usage under high load.
### Memory Track
The Memory Track shows Lynx page memory usage changes over time, with each Memory Track representing one Lynx page. Use it to analyze memory leaks, peaks, and abnormal growth.
## Common Track operations
### Pin Track
Pin Track fixes a Track at the top of the Trace view for easier comparison and continuous focus on key threads or events.
### Delete Track
Delete Track lets developers temporarily hide Tracks that are not of interest, simplifying the UI and leaving only the Tracks needed for analysis to improve efficiency. Deleted Tracks reappear after refreshing the Trace page.
## More resources
For a more detailed guide on Trace Tracks and advanced features, see the official Perfetto documentation: [perfetto-ui](https://perfetto.dev/docs/visualization/perfetto-ui)
---
url: /guide/performance/monitor-performance.md
---
# Performance Monitoring Overview
This overview helps you quickly build a mental model and understand common workflows for performance monitoring. It explains how the [Performance API](/guide/performance/monitor-performance/performance-api.md) and [Timing Flag](/guide/performance/monitor-performance/timing-flag.md) work together to create a closed loop from marking to analysis: `Mark` → `Collect` → `Report` → `Analyze`.
## Mental Model and Closed-Loop Workflow
The goal of monitoring is to ensure that critical moments in your page's lifecycle can be accurately recorded, reported, and analyzed, allowing you to quickly trace issues back to the source code or specific UI locations. Centered around the Lynx rendering pipeline and standardized events, we recommend the following closed-loop workflow:
- **Mark**: Use a Timing Flag (`__lynx_timing_flag`) on critical UI nodes or key data updates to define the rendering pipelines you need to monitor. The special flag `__lynx_timing_actual_fmp` can additionally generate the ActualFMP metric. For details, see [Marking Rendering Pipelines](/guide/performance/monitor-performance/timing-flag.md).
- **Collect**: Register a `PerformanceObserver` as early as possible on the front end (e.g., to listen for `metric.fcp`, `pipeline`, etc.), or consume events via asynchronous callbacks on the client side. For details, see [Collecting Performance Events](/guide/performance/monitor-performance/performance-api.md).
- **Report**: Report key metrics and events to your data platform using consistent naming and fields.
- **Analyze**: Analyze production data, using `PipelineEntry.identifier` (the Timing Flag) and event timelines to trace problems back to the corresponding modules or data flows for debugging and review.
Recommended minimum set of tracking points (for a basic production setup):
- **metric**: `fcp` (required), `actual_fmp` (when you need to measure the "first meaningful data paint")
- **pipeline**: `loadBundle` (for the initial screen rendering), and the pipelines associated with your critical Timing Flags.
## Feature Positioning
### [Performance API](/guide/performance/monitor-performance/performance-api.md)
- **Positioning**: A standardized performance interface covering four event types: `init`, `metric`, `pipeline`, and `resource`.
- **Triggering and Retrieval**: `PerformanceEntry` objects are generated by the Lynx Engine. The front end subscribes to them via `PerformanceObserver.observe([...])`, and the client receives them through the `onPerformanceEvent(entry)` callback on an asynchronous thread.
- **Typical Uses**:
- Obtaining standard metrics for initial screen rendering and key updates (FCP, ActualFMP).
- Combining multiple events to build custom business metrics (e.g., the waiting time from the end of the initial screen rendering to the first important data update).
### [Marking Lynx Pipeline](/guide/performance/monitor-performance/timing-flag.md)
- **Positioning**: A unique identifier used to mark a rendering pipeline for monitoring. It triggers the generation of a `PipelineEntry` and can also trigger a `MetricActualFmpEntry`.
- **Usage**:
- The recommended method is to set the `__lynx_timing_flag` attribute on a UI element.
- Setting the value to `__lynx_timing_actual_fmp` additionally generates an ActualFMP metric event.
- **Behavior and Limitations**:
- The same Timing Flag is only effective the first time it is encountered (repeated markings will not trigger the callback again).
- It is ineffective on tags that do not have a UI node, such as `
> The switch setting page is written in Lynx, and the DevTool component has already packaged the page.
Code example for integrating the devtool switch page:
> The switch setting page is written in Lynx, and the DevTool component has already packaged the page.
Code example for integrating the devtool switch page:
> The switch setting page is written in Lynx, and the DevTool component has already packaged the page.
Code example for integrating the devtool switch page:
1. Card Details
- The card can perform flip animation
- Here we'll learn how to use CSS animations to create smooth flip effects
2. Card List, wrapped in a [scroll-view](/api/elements/built-in/scroll-view.md) element
- Can scroll up and down to browse all cards
- When clicking a card, the top card details will update with corresponding card information
- Here we'll learn how to build an interactive scrolling list and how to pass data between components
**This is an example below: bankcards**
**Entry:** `src/final`
**Bundle:** `dist/final.lynx.bundle` | Web: `dist/final.web.bundle`
```tsx
// Copyright 2024 The Lynx Authors. All rights reserved.
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
import { root, useState } from "@lynx-js/react";
import Amount from "./Components/Amount.jsx";
import BankCardScrollView from "./Components/BankCardScrollView.jsx";
import type { BankCard } from "./Components/BankCardScrollView.jsx";
import BottomNode from "./Components/BottomNode.jsx";
import Card from "./Components/Card.jsx";
import "./index.scss";
function BankCards() {
const [selectedCard, setSelectedCard] = useState
Text with a gradient color
|
Filling background with radial-gradient
|
Create a 'fading edge' effect by adding linear-gradient
to
mask-image
property
|
clip-path to clip out a super-elliptical area
mask-image to create a circle area with fading edge
Each element tag consists of the following parts:
1. **Start tag**: Includes the name of the element tag (in this case, text) surrounded by angle brackets, indicating where the element tag begins.
2. **End tag**: Similar to the start tag but includes a forward slash before the element tag's name, indicating where the element tag ends.
3. **Content**: The content of the element tag, which for the `
## Built-in Elements
The Lynx Engine comes with some built-in elements by default to help you quickly build pages.
### View
The `
### Text
As mentioned earlier, the `
### Image
The `
### More Built-in Elements
For all built-in Lynx elements, refer to [Built-in Elements Documentation](/api/elements/built-in/view.md).
## Behind the Elements: Native Rendering
Lynx elements are designed to be platform-agnostic. They are rendered natively by the Lynx Engine into the UI primitives for each platforms, such as iOS and Android views, or HTML elements (including [custom elements](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements)) on the Web.
Lynx enables cross-platform application development based on the web technology, with its core being the establishment of a unified rendering system through element abstraction. Understanding the mapping relationship between the native views of the platform and Lynx elements is crucial to mastering the design concepts of elements within this framework. Below are some of the built-in elements and their corresponding concepts or analogues in different platforms:
` | Used for displaying text content. Specific text styles can be aligned. |
| [`` | Used for displaying different types of images, including web images, static resources, and local disk images. |
| [`
`](/api/elements/built-in/list.md) | `RecyclerView` | `UICollectionView` | `List` | None | High-performance scrollable element that reduces memory pressure through lazy loading and view reuse. |
| [`
box-sizing is border-box by default and Lynx does not exhibit the behavior of margin collapsing.}>
By default, the size properties of Lynx, such as `width`, `height`, and `max-width`, describe the size of the border box. This is inconsistent with the default behavior of the Web.
Lynx does not exhibit the behavior of [margin collapsing](https://developer.mozilla.org/en/docs/Web/CSS/CSS_box_model/Mastering_margin_collapsing) as in the Web.
| non-scrollable element | scrollable element | ||
|---|---|---|---|
|
|
|
|
|
|
overflow: hidden
|
scroll-orientation: vertical
scrollable element
|
scroll-orientation: horizontal
scrollable element
|
overflow:scrollfor scrolling effect !}>
In `Lynx`, the `view` component doesn't support the scrolling effect achieved by `overflow: scroll` as in the Web. Only scrolling containers like `<angle>
---
url: /api/css/data-type/color.md
---
# `<color>
---
url: /api/css/data-type/fit-content.md
---
# `<gradient>
---
url: /api/css/data-type/length-percentage.md
---
# `<length-percentage>
[`<length>
---
url: /api/css/data-type/max-content.md
---
# `<number>
---
url: /api/css/data-type/percentage.md
---
# `<percentage>
---
url: /api/css/data-type/string.md
---
# `<string>
---
url: /api/css/data-type/time.md
---
# `