2025-03-12 03:37:40 +00:00
---
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:
2025-03-27 02:49:13 +00:00
summary: React + Native Rendering
2025-03-12 03:37:40 +00:00
---
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"}};
2025-03-27 02:49:13 +00:00
[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.
2025-03-12 03:37:40 +00:00
[SheetJS ](https://sheetjs.com ) is a JavaScript library for reading and writing
data from spreadsheets.
:::caution Lynx support is considered experimental.
2025-03-27 02:49:13 +00:00
Lynx is relatively new and does not currently have a deep community.
Any issues should be reported to the Lynx project for further diagnosis.
2025-03-12 03:37:40 +00:00
:::
2025-03-27 02:49:13 +00:00
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:
2025-03-12 03:37:40 +00:00
- ["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:
< table > < thead > < tr >
< th > < a href = "#demo" > iOS< / a > < / th >
< th > < a href = "#demo" > Android< / a > < / th >
< / tr > < / thead > < tbody > < tr > < td >

< / td > < td >

< / td > < / tr > < / tbody > < / table >
:::caution pass
2025-03-27 02:49:13 +00:00
2025-03-12 03:37:40 +00:00
**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.
:::
2025-03-27 02:49:13 +00:00
:::danger pass
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
**Lynx development requires an Apple Silicon-powered Macintosh!**
[X64 is currently unsupported. ](https://github.com/lynx-family/lynx/issues/219 )
:::
## Integration Details
2025-03-12 03:37:40 +00:00
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.
< table >
< thead > < tr > < th > Spreadsheet< / th > < th > Array of Arrays< / th > < / tr > < / thead >
< tbody > < tr > < td >

< / td > < td >
```js
[
["Name", "Index"],
["Bill Clinton", 42],
["GeorgeW Bush", 43],
["Barack Obama", 44],
["Donald Trump", 45],
["Joseph Biden", 46]
]
```
< / td > < / tr > < / tbody > < / table >
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.
2025-03-27 02:49:13 +00:00
```ts title="State variables"
2025-03-12 03:37:40 +00:00
const [data, setData] = useState< any [ ] > ([
2025-03-27 02:49:13 +00:00
"SheetJS".split(""),
[5, 4, 3, 3, 7, 9, 5],
[8, 6, 7, 5, 3, 0, 9]
]);
2025-03-12 03:37:40 +00:00
const [widths, setWidths] = useState< number [ ] > (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:
2025-03-27 02:49:13 +00:00
```js title="Updating state from a workbook"
2025-03-12 03:37:40 +00:00
/* 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:
2025-03-27 02:49:13 +00:00
```js title="Calculating column widths"
2025-03-12 03:37:40 +00:00
/* 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
2025-03-27 02:49:13 +00:00
Lynx does not ship with a component for displaying tabular data.
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
The demo uses Lynx `<view/>` and `<text/>` elements to display tabular data:
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
```tsx title="Example JSX for displaying data"
2025-03-12 03:37:40 +00:00
{/* Table container */}
< view className = 'Table' >
2025-03-27 02:49:13 +00:00
{/* Map through each row in the data array */}
{data.map((row, rowIndex) => (
< view key = {`row-${rowIndex}`} className = "Row" >
{/* Map through each cell in the current row */}
{Array.isArray(row) & & row.map((cell, cellIndex) => (
{/* Cell with dynamic width based on content */}
< view
key={`cell-${rowIndex}-${cellIndex}`} className="Cell"
style={{ width: `${widths[cellIndex]}px` }}>
{/* Display cell content as text */}
< text > {String(cell)}< / text >
< / view >
))}
< / view >
))}
2025-03-12 03:37:40 +00:00
< / view >
```
## Fetching Remote Data
This snippet downloads and parses https://docs.sheetjs.com/pres.xlsx:
2025-03-27 02:49:13 +00:00
```js
2025-03-12 03:37:40 +00:00
/* 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 |
|:-----------|:--------------------|:---------|:-------------|:-------------|:-----------|
2025-03-27 02:49:13 +00:00
| 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 |
2025-03-12 03:37:40 +00:00
:::
:::danger Real Devices
2025-03-27 02:49:13 +00:00
When this demo was last tested, there was no simple standalone guide for running
Lynx apps on real devices.
2025-03-12 03:37:40 +00:00
:::
2025-03-27 02:49:13 +00:00
:::caution pass
First install Lynx by following the Guide![^1].
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
Make sure you can run a basic test app on your simulator before continuing!
2025-03-12 03:37:40 +00:00
:::
0) Install Lynx dependencies
1) Create project:
```bash
2025-03-27 02:49:13 +00:00
npm create rspeedy@0.8.6 -- -d SheetJSLynxFetch -t react-ts --tools biome
2025-03-12 03:37:40 +00:00
```
2) Install shared dependencies:
< CodeBlock language = "bash" > {`\
cd SheetJSLynxFetch
curl -o ./src/assets/SheetJS-logo.png https://docs.sheetjs.com/logo.png
2025-03-27 02:49:13 +00:00
npm i
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
2025-03-12 03:37:40 +00:00
< / CodeBlock >
2025-03-27 02:49:13 +00:00
3) Download [`App.tsx` ](pathname:///lynx/App.tsx ) into the `src` folder:
2025-03-12 03:37:40 +00:00
```bash
curl -o ./src/App.tsx https://docs.sheetjs.com/lynx/App.tsx
```
2025-03-27 02:49:13 +00:00
4) Download [`App.css` ](pathname:///lynx/App.css ) into the `src` folder:
2025-03-12 03:37:40 +00:00
```bash
curl -o ./src/App.css https://docs.sheetjs.com/lynx/App.css
```
< a id = "step5" > < / a >
2025-03-27 02:49:13 +00:00
5) Start the development server:
2025-03-12 03:37:40 +00:00
```bash
2025-03-27 02:49:13 +00:00
npm run dev
2025-03-12 03:37:40 +00:00
```
2025-03-27 02:49:13 +00:00
Keep the window open.
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
#### Android
6) Start the Android emulator:
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
< Tabs >
< TabItem name = "Android Studio" value = "Android Studio" >
2025-03-12 03:37:40 +00:00
In Android Studio, click "More actions" > "Virtual Device Manager". Look for the
emulated device in the list and click the ▶ button to play.
2025-03-27 02:49:13 +00:00
< / TabItem >
< TabItem name = "Android Studio" value = "Command Line" >
List the available emulators with `emulator -list-avds` :
2025-03-12 03:37:40 +00:00
```
shjs@sheetjs SheetJSLynxFetch % emulator -list-avds
Medium_Phone_API_35
^^^^^^^^^^^^^^^^^^^--- emulator name
```
2025-03-27 02:49:13 +00:00
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:
2025-03-12 03:37:40 +00:00
```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
```
:::
< / TabItem >
< / Tabs >
2025-03-27 02:49:13 +00:00
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:
2025-03-12 03:37:40 +00:00

2025-03-27 02:49:13 +00:00
10) In the emulator, open the "LynxExplorer" app.
11) In the **Enter Card URL** input field, paste the link. Tap **Go** .
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
The view will refresh. The app should look like the "Before" screenshot:
2025-03-12 03:37:40 +00:00
< table > < thead > < tr >
< th > Before< / th >
< th > After< / th >
< / tr > < / thead > < tbody > < tr > < td >

< / td > < td >

< / td > < / tr > < / tbody > < / table >
2025-03-27 02:49:13 +00:00
12) Tap "Import data from a spreadsheet" and verify that the app shows new data.
The app should look like the "After" screenshot.
2025-03-12 03:37:40 +00:00
**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.
:::
2025-03-27 02:49:13 +00:00
13) Download the LynxExplorer[^4] app tarball.
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
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 ).
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
14) Open `LynxExplorer-arm64.app.tar.gz` using Finder.
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
The tarball contains an app named `LynxExplorer-arm64` .
15) Launch the iOS Simulator.
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
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 ).
2025-03-12 03:37:40 +00:00

2025-03-27 02:49:13 +00:00
18) Tap the "LynxExplorer" icon in the simulator to launch the app.
2025-03-12 03:37:40 +00:00
2025-03-27 02:49:13 +00:00
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:
2025-03-12 03:37:40 +00:00
< table > < thead > < tr >
< th > Before< / th >
< th > After< / th >
< / tr > < / thead > < tbody > < tr > < td >

< / td > < td >

< / td > < / tr > < / tbody > < / table >
2025-03-27 02:49:13 +00:00
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"
2025-03-12 03:37:40 +00:00
[^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 )
2025-03-27 02:49:13 +00:00
[^4]: See ["LynxExplorer sandbox" ](https://github.com/lynx-family/lynx/releases/latest/ )