--- title: Sheets at Native Speed with Lynx sidebar_label: Lynx description: Build data-intensive mobile apps with Lynx. Seamlessly integrate spreadsheets into your app using SheetJS. Securely process and generate Excel files in the field. pagination_prev: demos/static/index pagination_next: demos/desktop/index sidebar_position: 7 sidebar_custom_props: summary: Lynx + Native Rendering --- import current from '/version.js'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import CodeBlock from '@theme/CodeBlock'; export const r = {style: {color:"red"}}; export const g = {style: {color:"green"}}; export const y = {style: {color:"gold"}}; export const gr = {style: {color:"gray"}}; [Lynx](https://lynxjs.org/) is a modern cross-platform framework. It builds iOS, Android and Web apps that use JavaScript for describing layouts and events. [SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing data from spreadsheets. :::caution Lynx support is considered experimental. Lynx is a great, fast, open-source alternative to React Native. Any issues should be reported to the Lynx project for further diagnosis. ::: This demo uses [ReactLynx](https://lynxjs.org/react) and SheetJS to process and generate spreadsheets. We'll explore how to load SheetJS in ReactLynx app in a few ways: - ["Fetching Remote Data"](#fetching-remote-data) uses the built-in `fetch` to download and parse remote workbook files. The "Fetching Remote Data" example creates an app that looks like the screenshots below:
iOS Android
![iOS screenshot](pathname:///lynx/react_lynx_fetch_demo_ios_1.jpeg) ![Android screenshot](pathname:///lynx/react_lynx_fetch_demo_android_1.png)
:::caution pass **Before testing this demo, follow the official React Lynx Guide!**[^1] Follow the instructions for iOS (requires macOS) and for Android. They will cover installation and system configuration. You should be able to build and run a sample app in the Android and the iOS (if applicable) simulators. ::: ## Integration Detail The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be imported from any component or script in the app. ### Internal State For simplicity, this demo uses an "Array of Arrays"[^2] as the internal state.
SpreadsheetArray of Arrays
![`pres.xlsx` data](pathname:///pres.png) ```js [ ["Name", "Index"], ["Bill Clinton", 42], ["GeorgeW Bush", 43], ["Barack Obama", 44], ["Donald Trump", 45], ["Joseph Biden", 46] ] ```
Each array represents a row in the table. This demo also keeps track of the column widths as a single array of numbers. The widths are used by the display component. ```tsx const [data, setData] = useState([ "SheetJS".split(""), [5, 4, 3, 3, 7, 9, 5], [8, 6, 7, 5, 3, 0, 9] ]); const [widths, setWidths] = useState(Array.from({ length: 7 }, () => 20)); ``` #### Updating State Starting from a SheetJS worksheet object, `sheet_to_json`[^3] with the `header` option can generate an array of arrays: ```js /* assuming `wb` is a SheetJS workbook */ function update_state(wb) { /* convert first worksheet to AOA */ const wsname = wb.SheetNames[0]; const ws = wb.Sheets[wsname]; const data = utils.sheet_to_json(ws, {header:1}); /* update state */ setData(data); /* update column widths */ setWidths(make_width(data)); } ``` _Calculating Column Widths_ Column widths can be calculated by walking each column and calculating the max data width. Using the array of arrays: ```js /* this function takes an array of arrays and generates widths */ function make_width(aoa) { /* walk each row */ aoa.forEach((r) => { /* walk each column */ r.forEach((c, C) => { /* update column width based on the length of the cell contents */ res[C] = Math.max(res[C]||60, String(c).length * 10); }); }); /* use a default value for columns with no data */ for(let C = 0; C < res.length; ++C) if(!res[C]) res[C] = 60; return res; } ``` ### Displaying Data The demo uses Lynx builtin element `` and `` to display the first worksheet. The demo uses components similar to the example below: ```tsx {/* Table container */} {/* Map through each row in the data array */} {data.map((row, rowIndex) => ( {/* Map through each cell in the current row */} {Array.isArray(row) && row.map((cell, cellIndex) => ( {/* Cell with dynamic width based on content */} {/* Display cell content as text */} {String(cell)} ))} ))} ``` ## Fetching Remote Data This snippet downloads and parses https://docs.sheetjs.com/pres.xlsx: ```tsx /* fetch data into an ArrayBuffer */ const ab = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer(); /* parse data */ const wb = XLSX.read(ab); ``` The `data.map()` approach allows direct rendering of the worksheet data in a tabular format, with each cell width dynamically calculated based on content. ### Fetch Demo :::note Tested Deployments This demo was tested in the following environments: **Simulators** | OS | Device | Lynx | LynxExplorer | Dev Platform | Date | |:-----------|:--------------------|:---------|:-------------|:-------------|:-----------| | Android 35 | Pixel 3a | `0.8.3` | `3.2.0-rc.0` | `darwin-arm` | 2025-03-11 | | iOS 18.3 | iPhone 16 Pro | `0.8.3` | `3.2.0-rc.0` | `darwin-arm` | 2025-03-11 | ::: :::danger Real Devices As of `2025-03-11`, there is no simple, standalone guide on how to build your Lynx app for real devices. ::: :::caution First install Lynx by following the Guide![^1]. Make sure you can run a basic test app on your simulator before continuing! ::: 0) Install Lynx dependencies 1) Create project: ```bash pnpm create rspeedy@0.8.3 -d SheetJSLynxFetch -t react-ts --tools biome ``` 2) Install shared dependencies: {`\ cd SheetJSLynxFetch curl -o ./src/assets/SheetJS-logo.png https://docs.sheetjs.com/logo.png pnpm install pnpm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} 3) Download [`App.tsx`](pathname:///lynx/App.tsx) and replace: ```bash curl -o ./src/App.tsx https://docs.sheetjs.com/lynx/App.tsx ``` 4) Download [`App.css`](pathname://lynx/App.css) and replace: ```bash curl -o ./src/App.css https://docs.sheetjs.com/lynx/App.css ``` 5) Start the development server, run: ```bash pnpm run dev ``` 6) Start the Android emulator:
Details (click to hide) **Android Studio** In Android Studio, click "More actions" > "Virtual Device Manager". Look for the emulated device in the list and click the ▶ button to play. **Command Line** List the available emulators with `emulator -list-avds` ``` shjs@sheetjs SheetJSLynxFetch % emulator -list-avds Medium_Phone_API_35 ^^^^^^^^^^^^^^^^^^^--- emulator name ``` Emulator name should be passed to `emulator -avd`. In a previous test, the name was `Medium_Phone_API_35` and the launch command was: ```bash emulator -avd Medium_Phone_API_35 ``` :::note pass On macOS, `~/Library/Android/sdk/emulator/` is the typical location for the `emulator` binary. If it cannot be found, add the folder to `PATH`: ```bash export PATH="$PATH":~/Library/Android/sdk/emulator emulator -avd Medium_Phone_API_35 ``` :::
7) While the Android emulator is open, download LynxExplorer[^4] - (Is a sandbox for trying out Lynx quickly) 1. Download the pre-build app from the [GitHub Release](https://github.com/lynx-family/lynx/releases/tag/3.2.0-rc.0) and select the APK `LynxExplorer-noasan-release.apk`. 2. Drag and drop the APK `LynxExplorer-noasan-release.apk` in to your Android simulator. --- 8) From [step 5](#step5), you will see a QR code appear in the terminal with a hyperlink like this. Copy the HTTP link. ![lynx live server link](pathname:///lynx/lynx_live_server_link.png) 9) In the simulator, open the _LynxExplorer_ app. In the input field labeled **Enter Card URL**, paste the link and click **Go**. 10) When opened, the app should look like the "Before" screenshot below. After tapping "Import data from a spreadsheet", verify that the app shows new data:
Before After
![before screenshot](pathname:///lynx/react_lynx_fetch_demo_android_1.png) ![after screenshot](pathname:///lynx/react_lynx_fetch_demo_android_2.png)
**iOS Testing** :::danger pass **iOS testing can only be performed on Apple hardware running macOS!** Xcode and iOS simulators are not available on Windows or Linux. ::: 1. Install Xcode open up the Mac App Store, search for [Xcode](https://apps.apple.com/us/app/xcode/id497799835), and click Install (or Update if you have it already). 2. Download [`LynxExplorer-arm64.app.tar.gz`](https://github.com/lynx-family/lynx/releases/latest/download/LynxExplorer-arm64.app.tar.gz). 3. Then, extract the downloaded archive: ```bash mkdir -p LynxExplorer-arm64.app/ tar -zxf LynxExplorer-arm64.app.tar.gz -C LynxExplorer-arm64.app/ ``` 4. Install LynxExplorer[^4] on simulator open Xcode, and choose Open Developer Tool from the Xcode menu. Click the Simulator to launch one. Drag "LynxExplorer-arm64.app" into it. 11) From [step 5](#step5), you will see a QR code appear in the terminal with a hyperlink like this. Copy the HTTP link. ![lynx live server link](pathname:///lynx/lynx_live_server_link.png) 12) In the simulator, open the _LynxExplorer_ app. In the input field labeled **Enter Card URL**, paste the link and click **Go**. 13) When opened, the app should look like the "Before" screenshot below. After tapping "Import data from a spreadsheet", verify that the app shows new data:
Before After
![before screenshot](pathname:///lynx/react_lynx_fetch_demo_ios_1.jpeg) ![after screenshot](pathname:///lynx/react_lynx_fetch_demo_ios_2.jpeg)
[^1]: Follow the ["Quick Start guide](https://lynxjs.org/guide/start/quick-start.html) and select the appropriate "Lynx Explorer sandbox" [^2]: See ["Array of Arrays" in the API reference](/docs/api/utilities/array#array-of-arrays) [^3]: See ["Array Output" in "Utility Functions"](/docs/api/utilities/array#array-output) [^4]: See ["LynxExplorer sandbox"](https://github.com/lynx-family/lynx/releases/tag/3.2.0-rc.0/)