---
title: NativeScript
pagination_prev: demos/static/index
pagination_next: demos/desktop/index
sidebar_position: 2
sidebar_custom_props:
  summary: JS + Native Elements
---

import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';

The [NodeJS Module](/docs/getting-started/installation/nodejs) can be imported
from the main entrypoint or any script in the project.

The "Complete 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>

![iOS screenshot](pathname:///nativescript/ios.png)

</td><td>

![Android screenshot](pathname:///nativescript/and.png)

</td></tr></tbody></table>

## Integration Details

The discussion covers the NativeScript + Angular integration.  Familiarity with
Angular and TypeScript is assumed.

The `@nativescript/core/file-system` package provides classes for file access.
The `File` class does not support binary data, but the file access singleton
from `@nativescript/core` does support reading and writing `ArrayBuffer`.

Reading and writing data require a URL.  The following snippet searches typical
document folders for a specified filename:

```ts
import { Folder, knownFolders, path } from '@nativescript/core/file-system';

function get_url_for_filename(filename: string): string {
  const target: Folder = knownFolders.documents() || knownFolders.ios.sharedPublic();
  return path.normalize(target.path + "///" + filename);
}
```

### Reading Local Files

`getFileAccess().readBufferAsync` can read data:

```ts
import { getFileAccess } from '@nativescript/core';
import { read } from 'xlsx';

/* find appropriate path */
const url = get_url_for_filename("SheetJSNS.xls");

/* get data */
const ab: ArrayBuffer = await getFileAccess().readBufferAsync(url);

/* read workbook */
const wb = read(ab);
```

### Writing Local Files

`getFileAccess().writeBufferAsync` can write data. iOS supports `Uint8Array`
directly but Android requires a true array of numbers:

```ts
import { getFileAccess } from '@nativescript/core';
import { write } from 'xlsx';

/* find appropriate path */
const url = get_url_for_filename("SheetJSNS.xls");

/* generate Uint8Array */
const u8: Uint8Array = write(wb, { bookType: 'xls', type: 'binary' });

/* attempt to save Uint8Array to file */
await getFileAccess().writeBufferAsync(url, global.isAndroid ? (Array.from(u8) as any) : u8);
```

### Fetching Remote Files

`getFile` from `@nativescript/core/http` can download files. After storing the
file in a temporary folder, `getFileAccess().readBufferAsync` can read the data:

```ts
import { knownFolders, path, getFileAccess } from '@nativescript/core'
import { getFile } from '@nativescript/core/http';
import { read } from 'xlsx';

/* generate temporary path for the new file */
const temp: string = path.join(knownFolders.temp().path, "pres.xlsx");

/* download file */
const file = await getFile("https://sheetjs.com/pres.xlsx", temp)

/* get data */
const ab: ArrayBuffer = await getFileAccess().readBufferAsync(file.path);

/* read workbook */
const wb = read(ab);
```

## Demo

:::note

This demo was tested on an Intel Mac on 2023 May 21.  NativeScript version
(as verified with `ns --version`) is `8.5.3`.

The iOS simulator runs iOS 16.2 on an iPhone 14 Pro Max.

The Android simulator runs Android 12.0 (S) API 31 on a Pixel 3.

:::

0) Follow the official Environment Setup instructions

### Base Project

1) Create a skeleton NativeScript + Angular app:

```bash
ns create SheetJSNS --ng
```

2) Launch the app in the iOS simulator to verify that the demo built properly:

```bash
cd SheetJSNS
ns run ios
```

(this may take a while)

Once the simulator launches and the test app is displayed, end the script by
selecting the terminal and entering the key sequence `CTRL + C`

3) From the project folder, install the library:

<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
</CodeBlock>

4) To confirm the library was loaded, change the title to show the version.  The
differences are highlighted.

`src/app/item/items.component.ts` should import the version string:

```ts title="src/app/item/items.component.ts"
// highlight-next-line
import { version } from 'xlsx';
import { Component, OnInit } from '@angular/core'

// ...

export class ItemsComponent implements OnInit {
  items: Array<Item>
  // highlight-next-line
  version = `SheetJS - ${version}`;

  constructor(private itemService: ItemService) {}
// ...
```

`src/app/item/items.component.html` should use the version in the title:

```xml title="src/app/item/items.component.html"
<!-- highlight-next-line -->
<ActionBar [title]="version"></ActionBar>

<GridLayout>
<!-- ... -->
```

Relaunch the app with `ns run ios` and the title bar should show the version.

![NativeScript Step 4](pathname:///mobile/nativescript4.png)

### Local Files

5) Add the Import and Export buttons to the template:

```xml title="src/app/item/items.component.html"
<ActionBar [title]="version"></ActionBar>

<!-- highlight-start -->
<StackLayout>
  <StackLayout orientation="horizontal">
    <Button text="Import File" (tap)="import()" style="padding: 10px"></Button>
    <Button text="Export File" (tap)="export()" style="padding: 10px"></Button>
  </StackLayout>
<!-- highlight-end -->
  <ListView [items]="items">
    <!-- ... -->
  </ListView>
<!-- highlight-next-line -->
</StackLayout>
```

```ts title="src/app/item/items.component.ts"
// highlight-start
import { version, utils, read, write } from 'xlsx';
import { Dialogs, getFileAccess } from '@nativescript/core';
import { Folder, knownFolders, path } from '@nativescript/core/file-system';
// highlight-end
import { Component, OnInit } from '@angular/core'

import { Item } from './item'
import { ItemService } from './item.service'

// highlight-start
function get_url_for_filename(filename: string): string {
  const target: Folder = knownFolders.documents() || knownFolders.ios.sharedPublic();
  return path.normalize(target.path + "///" + filename);
}
// highlight-end

@Component({
  selector: 'ns-items',
  templateUrl: './items.component.html',
})
export class ItemsComponent implements OnInit {
  items: Array<Item>
  version: string = `SheetJS - ${version}`;

  constructor(private itemService: ItemService) {}

  ngOnInit(): void {
    this.items = this.itemService.getItems()
  }

  // highlight-start
  /* Import button */
  async import() {
  }

  /* Export button */
  async export() {
  }
  // highlight-end
}
```

Restart the app process and two buttons should show up at the top:

![NativeScript Step 5](pathname:///mobile/nativescript5.png)

6) Implement import and export by adding the highlighted lines:

```ts title="src/app/item/items.component.ts"
  /* Import button */
  async import() {
    // highlight-start
    /* find appropriate path */
    const url = get_url_for_filename("SheetJSNS.xls");

    try {
      await Dialogs.alert(`Attempting to read from SheetJSNS.xls at ${url}`);
      /* get data */
      const ab: ArrayBuffer = await getFileAccess().readBufferAsync(url);

      /* read workbook */
      const wb = read(ab);

      /* grab first sheet */
      const wsname: string = wb.SheetNames[0];
      const ws = wb.Sheets[wsname];

      /* update table */
      this.items = utils.sheet_to_json<Item>(ws);
    } catch(e) { await Dialogs.alert(e.message); }
    // highlight-end
  }

  /* Export button */
  async export() {
    // highlight-start
    /* find appropriate path */
    const url = get_url_for_filename("SheetJSNS.xls");

    try {
      /* create worksheet from data */
      const ws = utils.json_to_sheet(this.items);

      /* create workbook from worksheet */
      const wb = utils.book_new();
      utils.book_append_sheet(wb, ws, "Sheet1");

      /* generate Uint8Array */
      const u8: Uint8Array = write(wb, { bookType: 'xls', type: 'buffer' });

      /* attempt to save Uint8Array to file */
      await getFileAccess().writeBufferAsync(url, global.isAndroid ? (Array.from(u8) as any) : u8);
      await Dialogs.alert(`Wrote to SheetJSNS.xls at ${url}`);
    } catch(e) { await Dialogs.alert(e.message); }
    // highlight-end
  }
```

### iOS

Relaunch the app with `ns run ios`

The app can be tested with the following sequence in the simulator:

- Tap "Export File".  A dialog will print where the file was written

- Open the file with a spreadsheet editor.

After the header row, insert a row with cell A2 = 0, B2 = SheetJS, C2 = Library:

```
id | name       | role
 0 | SheetJS    | Library
 1 | Ter Stegen | Goalkeeper
 3 | Piqué      | Defender
...
```

Restart the app after saving the file.

- Tap "Import File".  A dialog will print the path of the file that was read.
  The first item in the list will change:

![NativeScript Step 7](pathname:///mobile/nativescript7.png)

### Android

Launch the app with `ns run android`.  If the app does not automatically launch,
manually open the `SheetJSNS` app.

The app can be tested with the following sequence in the simulator:

- Tap "Export File". A dialog will print where the file was written. Typically
the URL is `/data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls`

- Pull the file from the simulator:

```bash
adb root
adb pull /data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls SheetJSNS.xls
```

- Open `SheetJSNS.xls` with a spreadsheet editor.

After the header row, insert a row with cell A2 = 0, B2 = SheetJS, C2 = Library:

```
id | name       | role
 0 | SheetJS    | Library
 1 | Ter Stegen | Goalkeeper
 3 | Piqué      | Defender
...
```

- Push the file back to the simulator:

```bash
adb push SheetJSNS.xls /data/user/0/org.nativescript.SheetJSNS/files/SheetJSNS.xls
```

- Tap "Import File".  A dialog will print the path of the file that was read.
  The first item in the list will change.

### Fetching Files

7) In `src/app/item/items.component.ts`, make `ngOnInit` asynchronous:

```ts title="src/app/item/items.component.ts"
  async ngOnInit(): Promise<void> {
    this.items = await this.itemService.getItems()
  }
```

8) Replace `item.service.ts` with the following:

```ts title="src/app/item/item.service.ts"
import { Injectable } from '@angular/core'

import { knownFolders, path, getFileAccess } from '@nativescript/core'
import { getFile } from '@nativescript/core/http';
import { read, utils  } from 'xlsx';

import { Item } from './item'
interface IPresident { Name: string; Index: number };

@Injectable({ providedIn: 'root' })
export class ItemService {
  private items: Array<Item>;

  async getItems(): Promise<Array<Item>> {
    /* fetch https://sheetjs.com/pres.xlsx */
    const temp: string = path.join(knownFolders.temp().path, "pres.xlsx");
    const ab = await getFile("https://sheetjs.com/pres.xlsx", temp)
    /* read the temporary file */
    const wb = read(await getFileAccess().readBufferAsync(ab.path));
    /* translate the first worksheet to the required Item type */
    const data = utils.sheet_to_json<IPresident>(wb.Sheets[wb.SheetNames[0]]);
    return this.items = data.map((pres, id) => ({id, name: pres.Name, role: ""+pres.Index} as Item));
  }

  getItem(id: number): Item {
    return this.items.filter((item) => item.id === id)[0]
  }
}
```

Relaunching the app in iOS or Android simulator should show Presidential data.