--- 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: React + 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 relatively new and does not currently have a deep community. Any issues should be reported to the Lynx project for further diagnosis. ::: This demo uses React (using [ReactLynx](https://lynxjs.org/react)) and SheetJS to process and generate spreadsheets. We'll explore how to load SheetJS in Lynx apps in the following scenarios: - ["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. ::: :::danger pass **Lynx development requires an Apple Silicon-powered Macintosh!** [X64 is currently unsupported.](https://github.com/lynx-family/lynx/issues/219) ::: ## Integration Details 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. ```ts title="State variables" 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 title="Updating state from a workbook" /* 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 title="Calculating column widths" /* 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 Lynx does not ship with a component for displaying tabular data. The demo uses Lynx `` and `` elements to display tabular data: ```tsx title="Example JSX for displaying data" {/* 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: ```js /* fetch data into an ArrayBuffer */ const ab = await (await fetch("https://docs.sheetjs.com/pres.xlsx")).arrayBuffer(); /* parse data */ const wb = XLSX.read(ab); ``` ### 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.6` | `3.2.0-rc.1` | `darwin-arm` | 2025-03-26 | | iOS 18.3 | iPhone 16 Pro | `0.8.6` | `3.2.0-rc.1` | `darwin-arm` | 2025-03-26 | ::: :::danger Real Devices When this demo was last tested, there was no simple standalone guide for running Lynx apps on real devices. ::: :::caution pass 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 npm create rspeedy@0.8.6 -- -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 npm i npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`} 3) Download [`App.tsx`](pathname:///lynx/App.tsx) into the `src` folder: ```bash curl -o ./src/App.tsx https://docs.sheetjs.com/lynx/App.tsx ``` 4) Download [`App.css`](pathname:///lynx/App.css) into the `src` folder: ```bash curl -o ./src/App.css https://docs.sheetjs.com/lynx/App.css ``` 5) Start the development server: ```bash npm run dev ``` Keep the window open. #### Android 6) Start the Android emulator: In Android Studio, click "More actions" > "Virtual Device Manager". Look for the emulated device in the list and click the ▶ button to play. List the available emulators with `emulator -list-avds`: ``` shjs@sheetjs SheetJSLynxFetch % emulator -list-avds Medium_Phone_API_35 ^^^^^^^^^^^^^^^^^^^--- emulator name ``` The 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) Download the LynxExplorer[^4] APK. The latest test used [`LynxExplorer-noasan-release.apk` for version `3.2.0-rc.1`](https://github.com/lynx-family/lynx/releases/download/3.2.0-rc.1/LynxExplorer-noasan-release.apk). 8) Drag and drop the APK into the Android emulator window. The emulator will install LynxExplorer. 9) In the terminal window from [step 5](#step5), copy the HTTP link. It will be printed below the QR code, as shown in the following screenshot: ![lynx live server link](pathname:///lynx/lynx_live_server_link.png) 10) In the emulator, open the "LynxExplorer" app. 11) In the **Enter Card URL** input field, paste the link. Tap **Go**. The view will refresh. The app should look like the "Before" screenshot:
Before After
![before screenshot](pathname:///lynx/react_lynx_fetch_demo_android_1.png) ![after screenshot](pathname:///lynx/react_lynx_fetch_demo_android_2.png)
12) Tap "Import data from a spreadsheet" and verify that the app shows new data. The app should look like the "After" screenshot. **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. ::: 13) Download the LynxExplorer[^4] app tarball. The latest test used [`LynxExplorer-arm64.app.tar.gz` for version `3.2.0-rc.1`](https://github.com/lynx-family/lynx/releases/download/3.2.0-rc.1/LynxExplorer-arm64.app.tar.gz). 14) Open `LynxExplorer-arm64.app.tar.gz` using Finder. The tarball contains an app named `LynxExplorer-arm64` . 15) Launch the iOS Simulator. 16) Click and drag `LynxExplorer-arm64` into the Simulator window. The simulator will install the "LynxExplorer" app. 17) Copy the HTTP link from the terminal window in [step 5](#step5). ![lynx live server link](pathname:///lynx/lynx_live_server_link.png) 18) Tap the "LynxExplorer" icon in the simulator to launch the app. 19) Tap the **Enter Card URL** input field and paste the link. Tap **Go**. The view will refresh. The app should look like the "Before" screenshot:
Before After
![before screenshot](pathname:///lynx/react_lynx_fetch_demo_ios_1.jpeg) ![after screenshot](pathname:///lynx/react_lynx_fetch_demo_ios_2.jpeg)
20) Tap "Import data from a spreadsheet" and verify that the app shows new data. The app should look like the "After" screenshot. [^1]: Follow ["Quick Start"](https://lynxjs.org/guide/start/quick-start.html) in the Lynx documentation 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/latest/)