For AI agents: the complete documentation index is available at /next/llms.txt, the full documentation bundle is available at /next/llms-full.txt, and this page is available as Markdown at /next/guide/inclusion/foldable-devices.md.
Lynx
  • English
  • Foldables

    At its September event, Apple unveiled its first foldable iPhone, iPhone Duo. Unfold the screen and the page you were reading gains a fresh canvas; fold it back and the content has to settle into a smaller space. The job of an app is to make the interface flow gracefully between the two.

    Lynx now fully supports foldable scenarios, proven in production inside TikTok. Our goal was simple: keep the integration effortless. The host passes new dimensions to Lynx, the frontend keeps its familiar layout tools, and the page reflows with the window on its own.

    A Lynx page in TikTok adjusts as the window shrinks

    Folding

    A Lynx page in TikTok adjusts as the window grows

    Unfolding

    New screen sizes

    Adaptation starts with the host. When the window changes size, the host finishes native layout first, then updates Lynx's screen metrics, viewport, and GlobalProps together, keeping the engine's sizing bases in step with the dimensions the frontend reads.

    Start with the data the frontend needs. GlobalProps carries environment information from the host to the page, which reads it through lynx.__globalProps. In TikTok, we distinguish the window's dimensions from the space available to an individual page. The community can use the same convention:

    FieldMeaning
    viewportWidth, viewportHeightThe current LynxView's actual layout dimensions, for page layout
    screenWidth, screenHeightThe current application window's dimensions; a LynxView in a dialog or card may be smaller

    These host-defined fields use logical pixels: Lynx CSS px, corresponding to iOS pt. Set initial values through LynxLoadMeta.globalProps, then update each LynxView as its dimensions change. The frontend treats them as read-only.

    Updates merge the fields and emit onGlobalPropsChanged. With the default globalPropsMode: 'reactive', ReactLynx automatically re-renders the entire page; components simply read the latest dimensions during render. The optional 'event' mode disables this automatic render.

    The host also refreshes screen metrics for rpx and the viewport for vw and vh. ReactLynx applies UI changes through Element PAPI, then the engine resolves styles and runs Layout using the new dimensions. The platform paints the result.

    From a size change to the screenDefault reactive mode
    1. Host update
      screen metricsviewportGlobalProps
      Sync dimensions together
    2. ReactLynxAutomatic re-renderRead new dimensions and compute UI changes
    3. Element PAPIApply changes to the Element tree
    4. Resolve · LayoutResolve styles and calculate sizes and positions
    5. PaintApply UI updates and paint the new frame
    GlobalProps drives re-rendering; screen metrics and the viewport supply layout dimensions.

    On iOS, this uses updateScreenMetricsWithWidth:height:, updateViewport, and updateGlobalPropsWithDictionary. Once the view is attached and native layout is complete, call them on the main thread. Here, screen metrics use the window size, and the viewport uses the LynxView's size:

    CGSize windowSize = lynxView.window.bounds.size;
    CGSize viewportSize = lynxView.bounds.size;
    
    [lynxView updateScreenMetricsWithWidth:windowSize.width
                                   height:windowSize.height];
    [lynxView updateViewportWithPreferredLayoutWidth:viewportSize.width
                             preferredLayoutHeight:viewportSize.height
                                        needLayout:YES];
    [lynxView updateGlobalPropsWithDictionary:@{
      @"screenWidth": @(windowSize.width),
      @"screenHeight": @(windowSize.height),
      @"viewportWidth": @(viewportSize.width),
      @"viewportHeight": @(viewportSize.height),
    }];

    Initialize the same rpx basis through LynxViewBuilder.screenSize, and skip unchanged dimensions. Screen metrics updates do not trigger layout themselves; the example uses needLayout:YES. With enableAutoLayout enabled, constraints drive viewport updates.

    Familiar layout units

    Once this flow is connected, frontend adaptation usually takes little work. For pages that already use flexible layouts and relative units, the frontend does not need to detect the folding state or trigger updates manually — the layout adjusts to the newly available space on its own.

    That leaves a single decision: what each dimension should follow — its parent container, the whole viewport, or the size of the text. Pick the right reference, and your existing responsive layout keeps doing its job.

    In the table below, automatic adaptation describes how each unit follows its sizing basis once the host integration is in place.

    UnitAutomatic adaptationSizing basis and guidance
    %Yes, follows the parentWidth and height are relative to the containing block, usually the parent. Suitable for cards and dialogs; percentage heights require a definite parent height
    vw, vhYes, follows the viewport1% of viewport width or height. Suitable for page-level dimensions; prefer % inside nested components
    rpxYes, follows screen metrics1/750 of the logical width configured in screen metrics
    px, ppxNo, fixed pixel unitsLogical and physical pixels, respectively. Keep px for font sizes, spacing, and controls; use ppx for pixel-level details
    rem, emFollows font sizeRelative to the root or current element's font size, respectively; em in font-size uses the inherited font size. Window resizing alone does not scale these units

    In practice, Flex and width: 100% make a good starting point, with max-width to keep content from stretching too far on wider screens. Font sizes, buttons, and spacing can keep sensible px values, so the content area grows with the screen while reading and interaction stay a constant size.

    rpx follows screen metrics: 1rpx equals 1/750 of the configured logical width. When the host updates screen metrics, Lynx recalculates rpx values during layout, so the frontend does not need to recompute them.

    export function ResponsiveCard() {
      return (
        <view
          style={{
            display: 'flex',
            flexDirection: 'column',
            width: '100%', // Follow the parent.
            maxWidth: '600px', // Limit content width when the foldable screen is unfolded.
            padding: '16px', // Keep spacing and font size stable.
            gap: '12px',
          }}
        >
          <text style={{ fontSize: '16px' }}>A familiar layout</text>
          <view
            style={{
              width: '100%',
              height: '200rpx', // Recalculated when the host updates screen metrics.
              backgroundColor: '#e6f4ff',
            }}
          />
        </view>
      );
    }

    See length units for more detail.

    Except as otherwise noted, this work is licensed under a Creative Commons Attribution 4.0 International License, and code samples are licensed under the Apache License 2.0.