2023-01-15 03:36:13 +00:00
|
|
|
---
|
|
|
|
title: NextJS
|
2023-02-28 11:40:44 +00:00
|
|
|
pagination_prev: demos/net/index
|
|
|
|
pagination_next: demos/mobile/index
|
2023-01-15 03:36:13 +00:00
|
|
|
---
|
|
|
|
|
2023-04-27 09:12:19 +00:00
|
|
|
import current from '/version.js';
|
2023-05-26 22:50:23 +00:00
|
|
|
import Tabs from '@theme/Tabs';
|
|
|
|
import TabItem from '@theme/TabItem';
|
2023-04-29 11:21:37 +00:00
|
|
|
import CodeBlock from '@theme/CodeBlock';
|
2023-04-27 09:12:19 +00:00
|
|
|
|
2023-06-02 02:17:56 +00:00
|
|
|
NextJS is a server-side framework for building static and dynamic sites. For
|
|
|
|
pure static sites, [Webpack loaders](/docs/demos/static/webpack) can preprocess
|
|
|
|
files and NextJS can build static pages from spreadsheets. For dynamic sites,
|
|
|
|
NextJS lifecycle methods can read files on the server side.
|
2023-01-15 03:36:13 +00:00
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
The [NodeJS module](/docs/getting-started/installation/nodejs) can be imported
|
|
|
|
from pages or loaded in Webpack loaders.
|
|
|
|
|
|
|
|
:::warning
|
|
|
|
|
|
|
|
[`import`](/docs/getting-started/installation/nodejs#esm-import) does not load
|
|
|
|
NodeJS native modules. The Installation section includes a note on dynamic
|
|
|
|
import of `fs` within lifecycle methods.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
NextJS best practices have evolved over time, but there are three key parts:
|
|
|
|
|
|
|
|
1) [Loading Data](#loading-data): NextJS can read files in lifecycle methods OR
|
|
|
|
custom Webpack loaders can create asset modules.
|
|
|
|
|
|
|
|
2) [Lifecycle Methods](#nextjs-strategies): NextJS includes strategies for
|
|
|
|
static pages (`getStaticProps`) as well as dynamic pages (`getServerSideProps`).
|
|
|
|
|
|
|
|
3) [Data Presentation](#data-presentation): Pages use React and JSX.
|
|
|
|
|
|
|
|
:::caution Next 13+ and SWC
|
|
|
|
|
|
|
|
Next 13 switched to the SWC minifier. There are known issues with the minifier.
|
|
|
|
Until those issues are resolved, SWC should be disabled in `next.config.js`:
|
|
|
|
|
|
|
|
```js title="next.config.js"
|
|
|
|
module.exports = {
|
|
|
|
// highlight-next-line
|
|
|
|
swcMinify: false
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
2023-06-02 02:17:56 +00:00
|
|
|
:::warning Telemetry
|
|
|
|
|
|
|
|
NextJS collects telemetry by default. The `telemetry` subcommand can disable it:
|
|
|
|
|
|
|
|
```js
|
2023-07-23 21:01:30 +00:00
|
|
|
npx next@13.4.12 telemetry disable
|
2023-06-02 02:17:56 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
The setting can be verified by running
|
|
|
|
|
|
|
|
```js
|
2023-07-23 21:01:30 +00:00
|
|
|
npx next@13.4.12 telemetry status
|
2023-06-02 02:17:56 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
:::note
|
|
|
|
|
|
|
|
The following deployments were tested:
|
|
|
|
|
2023-07-23 21:01:30 +00:00
|
|
|
| NextJS | NodeJS | Date |
|
|
|
|
|:--------|:----------|:-----------|
|
|
|
|
| 11.1.4 | `16.20.1` | 2023-07-23 |
|
|
|
|
| 12.3.4 | `18.17.0` | 2023-07-23 |
|
|
|
|
| 13.4.12 | `18.17.0` | 2023-07-23 |
|
2023-06-02 02:17:56 +00:00
|
|
|
|
|
|
|
:::
|
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
## Loading Data
|
2023-01-15 03:36:13 +00:00
|
|
|
|
|
|
|
At a high level, there are two ways to pull spreadsheet data into NextJS apps:
|
|
|
|
loading an asset module or performing the file read operations from the NextJS
|
2023-05-26 22:50:23 +00:00
|
|
|
lifecycle methods.
|
|
|
|
|
|
|
|
Asset modules are appropriate for static sites when the file names are known in
|
|
|
|
advance. Performing file read operations in lifecycle methods is more flexible
|
|
|
|
but does not support live reloading.
|
|
|
|
|
|
|
|
### Asset Module
|
|
|
|
|
|
|
|
:::caution
|
|
|
|
|
|
|
|
When the demo was last tested, Turbopack did not support true raw loaders. For
|
|
|
|
development use, the normal `npx next dev` should be used.
|
2023-01-15 03:36:13 +00:00
|
|
|
|
|
|
|
:::
|
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
The following diagram depicts the workbook waltz:
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
flowchart LR
|
|
|
|
file[(workbook\nfile)]
|
|
|
|
subgraph SheetJS operations
|
|
|
|
base64(base64\nstring)
|
|
|
|
aoo(array of\nobjects)
|
|
|
|
end
|
|
|
|
html{{HTML\nTABLE}}
|
|
|
|
file --> |base64-loader.js\ncustom plugin| base64
|
|
|
|
base64 --> |page\nlifecycle method| aoo
|
|
|
|
aoo --> |page\nIndex method| html
|
|
|
|
```
|
2023-01-15 03:36:13 +00:00
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
In this flow, it is strongly recommended to make a loader return a Base64 string:
|
2023-01-15 03:36:13 +00:00
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
```js title="base64-loader.js"
|
|
|
|
function loader(content) {
|
|
|
|
/* since `loader.raw` is true, `content` is a Buffer */
|
|
|
|
return `export default '${content.toString("base64")}'`;
|
|
|
|
}
|
|
|
|
/* ensure the function receives a Buffer */
|
|
|
|
loader.raw = true;
|
|
|
|
module.exports = loader;
|
|
|
|
```
|
|
|
|
|
|
|
|
The webpack configuration is controlled in `next.config.js`:
|
|
|
|
|
|
|
|
```js title="next.config.js"
|
|
|
|
module.exports = {
|
|
|
|
webpack: (config) => {
|
|
|
|
// highlight-start
|
|
|
|
/* add to the webpack config module.rules array */
|
|
|
|
config.module.rules.push({
|
|
|
|
/* `test` matches file extensions */
|
|
|
|
test: /\.(numbers|xls|xlsx|xlsb)/,
|
|
|
|
/* use the loader script */
|
|
|
|
use: [ { loader: './base64-loader' } ]
|
|
|
|
});
|
|
|
|
// highlight-end
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
Module alias directories can be defined in `jsconfig.json` or `tsconfig.json`:
|
|
|
|
|
|
|
|
```json title="jsconfig.json"
|
|
|
|
{
|
|
|
|
"compilerOptions": {
|
|
|
|
"baseUrl": ".",
|
|
|
|
"paths": {
|
|
|
|
// highlight-next-line
|
|
|
|
"@/*": ["*"]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Pages can import the files directly. It is strongly recommended to store files
|
|
|
|
in a `data` folder. This example uses `getStaticProps` to parse `sheetjs.xlsx`:
|
|
|
|
|
|
|
|
```jsx title="index.js"
|
|
|
|
import { read, utils } from 'xlsx';
|
2023-01-15 03:36:13 +00:00
|
|
|
// highlight-next-line
|
2023-05-26 22:50:23 +00:00
|
|
|
import base64 from '@/data/sheetjs.xlsx';
|
|
|
|
|
|
|
|
export async function getStaticProps() {
|
|
|
|
/* parse base64 data */
|
|
|
|
// highlight-next-line
|
|
|
|
const wb = read(base64, { type: "base64" });
|
|
|
|
return { props: {
|
|
|
|
/* generate array of objects from the first sheet */
|
|
|
|
data: utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]])
|
|
|
|
} };
|
|
|
|
}
|
2023-01-15 03:36:13 +00:00
|
|
|
```
|
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
### Raw Operations
|
2023-01-15 03:36:13 +00:00
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
Files can be read using `readFile` in lifecycle methods. The `cwd` method from
|
|
|
|
the `process` module will point to the root of the project.
|
|
|
|
|
|
|
|
The following diagram depicts the workbook waltz:
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
flowchart LR
|
|
|
|
file[(workbook\nfile)]
|
|
|
|
subgraph SheetJS operations
|
|
|
|
buffer(NodeJS\nBuffer)
|
|
|
|
aoo(array of\nobjects)
|
|
|
|
end
|
|
|
|
html{{HTML\nTABLE}}
|
|
|
|
file --> |page\nlifecycle method| buffer
|
|
|
|
buffer --> |page\nlifecycle method| aoo
|
|
|
|
aoo --> |page\nIndex method| html
|
|
|
|
```
|
|
|
|
|
|
|
|
This example reads the file `sheetjs.xlsx` in the `data` folder in the project:
|
2023-01-15 03:36:13 +00:00
|
|
|
|
|
|
|
```js
|
|
|
|
import { readFile, utils, set_fs } from 'xlsx';
|
|
|
|
import { join } from 'path';
|
|
|
|
import { cwd } from 'process';
|
|
|
|
|
|
|
|
export async function getServerSideProps() {
|
2023-05-26 22:50:23 +00:00
|
|
|
// highlight-start
|
2023-01-15 03:36:13 +00:00
|
|
|
set_fs(await import("fs")); // dynamically import 'fs' when needed
|
2023-05-26 22:50:23 +00:00
|
|
|
const filename = join(cwd(), "data", "sheetjs.xlsx"); // /data/sheetjs.xlsx
|
|
|
|
const wb = readFile(filename);
|
|
|
|
// highlight-end
|
|
|
|
|
|
|
|
/* generate and return the html from the first worksheet */
|
|
|
|
const data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]);
|
|
|
|
return { props: { data } };
|
2023-01-15 03:36:13 +00:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
:::warning Reading and writing files during the build process
|
2023-01-15 03:36:13 +00:00
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
As the NextJS workaround is non-traditional, it bears repeating:
|
2023-01-15 03:36:13 +00:00
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
`fs` cannot be statically imported from the top level in NextJS pages. The
|
|
|
|
dynamic import must happen within a lifecycle function.
|
2023-01-15 03:36:13 +00:00
|
|
|
|
|
|
|
:::
|
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
|
2023-01-15 03:36:13 +00:00
|
|
|
## NextJS Strategies
|
|
|
|
|
|
|
|
NextJS currently provides 3 strategies:
|
|
|
|
|
|
|
|
- "Static Site Generation" using `getStaticProps`
|
|
|
|
- "SSG with Dynamic Routes" using `getStaticPaths`
|
|
|
|
- "Server-Side Rendering" using `getServerSideProps`
|
|
|
|
|
|
|
|
### Static Site Generation
|
|
|
|
|
|
|
|
When using `getStaticProps`, the file will be read once during build time.
|
2023-05-26 22:50:23 +00:00
|
|
|
This example reads `sheetjs.xlsx` from the `data` folder:
|
|
|
|
|
|
|
|
<Tabs groupId="data">
|
|
|
|
<TabItem value="asset" label="Asset Module">
|
|
|
|
|
|
|
|
```js
|
|
|
|
import { read, utils } from 'xlsx';
|
|
|
|
import base64 from '@/data/sheetjs.xlsx';
|
|
|
|
|
|
|
|
export async function getStaticProps() {
|
|
|
|
const wb = read(base64, { type: "base64" });
|
|
|
|
|
|
|
|
/* generate and return the html from the first worksheet */
|
|
|
|
const html = utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
|
|
|
|
return { props: { html } };
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="raw" label="Raw Operations">
|
2023-01-15 03:36:13 +00:00
|
|
|
|
|
|
|
```js
|
|
|
|
import { readFile, set_fs, utils } from 'xlsx';
|
2023-05-26 22:50:23 +00:00
|
|
|
import { join } from 'path';
|
|
|
|
import { cwd } from 'process';
|
2023-01-15 03:36:13 +00:00
|
|
|
|
|
|
|
export async function getStaticProps() {
|
|
|
|
set_fs(await import("fs"));
|
2023-05-26 22:50:23 +00:00
|
|
|
const filename = join(cwd(), "data", "sheetjs.xlsx"); // /data/sheetjs.xlsx
|
|
|
|
const wb = readFile(filename);
|
2023-01-15 03:36:13 +00:00
|
|
|
|
|
|
|
/* generate and return the html from the first worksheet */
|
|
|
|
const html = utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
|
|
|
|
return { props: { html } };
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
</TabItem>
|
|
|
|
</Tabs>
|
|
|
|
|
2023-01-15 03:36:13 +00:00
|
|
|
### Dynamic Routes
|
|
|
|
|
|
|
|
Typically a static site with dynamic routes has an endpoint `/sheets/[id]` that
|
|
|
|
implements both `getStaticPaths` and `getStaticProps`.
|
|
|
|
|
|
|
|
- `getStaticPaths` should return an array of worksheet indices:
|
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
<Tabs groupId="data">
|
|
|
|
<TabItem value="asset" label="Asset Module">
|
|
|
|
|
2023-01-15 03:36:13 +00:00
|
|
|
```js
|
2023-05-26 22:50:23 +00:00
|
|
|
import { read } from 'xlsx';
|
|
|
|
import base64 from '@/data/sheetjs.xlsx';
|
|
|
|
|
|
|
|
export async function getStaticPaths() {
|
|
|
|
/* read file */
|
|
|
|
const wb = read(base64, { type: "base64" });
|
|
|
|
|
|
|
|
/* generate an array of objects that will be used for generating pages */
|
|
|
|
const paths = wb.SheetNames.map((name, idx) => ({ params: { id: idx.toString() } }));
|
|
|
|
return { paths, fallback: false };
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="raw" label="Raw Operations">
|
|
|
|
|
|
|
|
```js
|
|
|
|
import { readFile, set_fs } from 'xlsx';
|
|
|
|
import { join } from 'path';
|
|
|
|
import { cwd } from 'process';
|
|
|
|
|
2023-01-15 03:36:13 +00:00
|
|
|
export async function getStaticPaths() {
|
|
|
|
/* read file */
|
|
|
|
set_fs(await import("fs"));
|
2023-05-26 22:50:23 +00:00
|
|
|
const filename = join(cwd(), "data", "sheetjs.xlsx"); // /data/sheetjs.xlsx
|
2023-01-15 03:36:13 +00:00
|
|
|
const wb = readFile(path);
|
|
|
|
|
|
|
|
/* generate an array of objects that will be used for generating pages */
|
|
|
|
const paths = wb.SheetNames.map((name, idx) => ({ params: { id: idx.toString() } }));
|
|
|
|
return { paths, fallback: false };
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
</TabItem>
|
|
|
|
</Tabs>
|
|
|
|
|
2023-01-15 03:36:13 +00:00
|
|
|
:::note
|
|
|
|
|
|
|
|
For a pure static site, `fallback` must be set to `false`!
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
- `getStaticProps` will generate the actual HTML for each page:
|
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
<Tabs groupId="data">
|
|
|
|
<TabItem value="asset" label="Asset Module">
|
|
|
|
|
2023-01-15 03:36:13 +00:00
|
|
|
```js
|
2023-05-26 22:50:23 +00:00
|
|
|
import { read, utils } from 'xlsx';
|
|
|
|
import base64 from '@/data/sheetjs.xlsx';
|
|
|
|
|
|
|
|
export async function getStaticProps(ctx) {
|
|
|
|
/* read file */
|
|
|
|
const wb = read(base64, { type: "base64" });
|
|
|
|
|
|
|
|
/* get the corresponding worksheet and generate HTML */
|
|
|
|
const ws = wb.Sheets[wb.SheetNames[ctx.params.id]]; // id from getStaticPaths
|
|
|
|
const html = utils.sheet_to_html(ws);
|
|
|
|
return { props: { html } };
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="raw" label="Raw Operations">
|
|
|
|
|
|
|
|
```js
|
|
|
|
import { readFile, set_fs, utils } from 'xlsx';
|
|
|
|
import { join } from 'path';
|
|
|
|
import { cwd } from 'process';
|
|
|
|
|
2023-01-15 03:36:13 +00:00
|
|
|
export async function getStaticProps(ctx) {
|
|
|
|
/* read file */
|
|
|
|
set_fs(await import("fs"));
|
2023-05-26 22:50:23 +00:00
|
|
|
const filename = join(cwd(), "data", "sheetjs.xlsx"); // /data/sheetjs.xlsx
|
2023-01-15 03:36:13 +00:00
|
|
|
const wb = readFile(path);
|
|
|
|
|
|
|
|
/* get the corresponding worksheet and generate HTML */
|
|
|
|
const ws = wb.Sheets[wb.SheetNames[ctx.params.id]]; // id from getStaticPaths
|
|
|
|
const html = utils.sheet_to_html(ws);
|
|
|
|
return { props: { html } };
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
</TabItem>
|
|
|
|
</Tabs>
|
|
|
|
|
2023-01-15 03:36:13 +00:00
|
|
|
### Server-Side Rendering
|
|
|
|
|
|
|
|
:::caution Do not use on a static site
|
|
|
|
|
|
|
|
These routes require a NodeJS dynamic server. Static page generation will fail!
|
|
|
|
|
|
|
|
`getStaticProps` and `getStaticPaths` support static site generation (SSG).
|
|
|
|
|
|
|
|
`getServerSideProps` is suited for NodeJS hosted deployments where the workbook
|
|
|
|
changes frequently and a static site is undesirable.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
When using `getServerSideProps`, the file will be read on each request.
|
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
<Tabs groupId="data">
|
|
|
|
<TabItem value="asset" label="Asset Module">
|
|
|
|
|
|
|
|
:::caution Consider using a static strategy
|
|
|
|
|
|
|
|
When using asset modules, the file names and file paths are processed during the
|
|
|
|
build step. The content is fixed. In this situation, a static approach such as
|
|
|
|
`getStaticProps` is strongly recommended.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
```js
|
|
|
|
import { read } from 'xlsx';
|
|
|
|
import base64 from '@/data/sheetjs.xlsx';
|
|
|
|
|
|
|
|
export async function getServerSideProps() {
|
|
|
|
/* read file */
|
|
|
|
const wb = read(base64, { type: "base64" });
|
|
|
|
|
|
|
|
/* generate and return the html from the first worksheet */
|
|
|
|
const html = utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
|
|
|
|
return { props: { html } };
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
</TabItem>
|
|
|
|
<TabItem value="raw" label="Raw Operations">
|
|
|
|
|
2023-01-15 03:36:13 +00:00
|
|
|
```js
|
|
|
|
import { readFile, set_fs, utils } from 'xlsx';
|
2023-05-26 22:50:23 +00:00
|
|
|
import { join } from 'path';
|
|
|
|
import { cwd } from 'process';
|
2023-01-15 03:36:13 +00:00
|
|
|
|
|
|
|
export async function getServerSideProps() {
|
|
|
|
/* read file */
|
|
|
|
set_fs(await import("fs"));
|
2023-05-26 22:50:23 +00:00
|
|
|
const filename = join(cwd(), "data", "sheetjs.xlsx"); // /data/sheetjs.xlsx
|
|
|
|
const wb = readFile(path);
|
2023-01-15 03:36:13 +00:00
|
|
|
|
|
|
|
/* generate and return the html from the first worksheet */
|
|
|
|
const html = utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]);
|
|
|
|
return { props: { html } };
|
|
|
|
};
|
2023-05-26 22:50:23 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
</TabItem>
|
|
|
|
</Tabs>
|
|
|
|
|
|
|
|
## Data Presentation
|
|
|
|
|
|
|
|
[The React demo](/docs/demos/frontend/react) compares common approaches.
|
|
|
|
|
|
|
|
### HTML
|
|
|
|
|
|
|
|
HTML output can be generated using `XLSX.utils.sheet_to_html` and inserted into
|
|
|
|
the document using the `dangerouslySetInnerHTML` attribute:
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
flowchart LR
|
|
|
|
subgraph SheetJS operations
|
|
|
|
data(File\nData)
|
|
|
|
code{{HTML\nTABLE}}
|
|
|
|
end
|
|
|
|
html{{Rendered\nPage}}
|
|
|
|
data --> |lifecycle\nsheet_to_html| code
|
|
|
|
code --> |Index\ninnerHTML| html
|
|
|
|
```
|
|
|
|
|
|
|
|
```jsx
|
|
|
|
export default function Index({html, type}) { return (
|
|
|
|
<div dangerouslySetInnerHTML={{ __html: html }} />
|
|
|
|
); }
|
|
|
|
```
|
|
|
|
|
|
|
|
### Arrays of Objects
|
|
|
|
|
|
|
|
Arrays of objects can be generated using `XLSX.utils.sheet_to_json` and inserted
|
|
|
|
into the document using standard JSX:
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
flowchart LR
|
|
|
|
subgraph SheetJS operations
|
|
|
|
data(File\nData)
|
|
|
|
aoo(array of\nobjects)
|
|
|
|
end
|
|
|
|
html{{Rendered\nPage}}
|
|
|
|
data --> |lifecycle\nsheet_to_json| aoo
|
|
|
|
aoo --> |Index\nReact + JSX| html
|
|
|
|
```
|
|
|
|
|
|
|
|
```jsx
|
|
|
|
export default function Index({aoo, type}) { return (
|
|
|
|
<table><thead><tr key={0}><th>Name</th><th>Index</th></tr></thead><tbody>
|
|
|
|
// highlight-start
|
|
|
|
{aoo.map(row => ( <tr>
|
|
|
|
<td>{row.Name}</td>
|
|
|
|
<td>{row.Index}</td>
|
|
|
|
</tr>))}
|
|
|
|
// highlight-end
|
|
|
|
</tbody></table>
|
|
|
|
); }
|
2023-01-15 03:36:13 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
## Demo
|
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
:::note
|
|
|
|
|
|
|
|
This demo showcases the following SheetJS + NextJS flows:
|
|
|
|
|
|
|
|
| Page | Loading Data | Lifecycle Method | SheetJS API |
|
|
|
|
|:----------------------|:-------------|:---------------------|:----------------|
|
|
|
|
| `/getStaticProps` | asset module | `getStaticProps` | `sheet_to_json` |
|
|
|
|
| `/sheets/[id]` | asset module | `getStaticPaths` | `sheet_to_html` |
|
|
|
|
| `/getServerSideProps` | lifecycle | `getServerSideProps` | `sheet_to_html` |
|
|
|
|
|
2023-07-23 21:01:30 +00:00
|
|
|
The commands in this demo use `next@13.4.12`. Other versions were tested by
|
|
|
|
replacing the version number in the relevant commands.
|
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
:::
|
|
|
|
|
|
|
|
### Initial Setup
|
|
|
|
|
2023-01-15 03:36:13 +00:00
|
|
|
0) Disable NextJS telemetry:
|
|
|
|
|
|
|
|
```js
|
2023-07-23 21:01:30 +00:00
|
|
|
npx next@13.4.12 telemetry disable
|
2023-01-15 03:36:13 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
Confirm it is disabled by running
|
|
|
|
|
|
|
|
```js
|
2023-07-23 21:01:30 +00:00
|
|
|
npx next@13.4.12 telemetry status
|
2023-01-15 03:36:13 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
1) Set up folder structure. At the end, a `pages` folder with a `sheets`
|
|
|
|
subfolder must be created. On Linux or MacOS or WSL:
|
|
|
|
|
|
|
|
```bash
|
2023-05-26 22:50:23 +00:00
|
|
|
mkdir sheetjs-next
|
|
|
|
cd sheetjs-next
|
2023-01-15 03:36:13 +00:00
|
|
|
mkdir -p pages/sheets/
|
|
|
|
```
|
|
|
|
|
|
|
|
2) Download the [test file](pathname:///next/sheetjs.xlsx) and place in the
|
|
|
|
project root. On Linux or MacOS or WSL:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
curl -LO https://docs.sheetjs.com/next/sheetjs.xlsx
|
|
|
|
```
|
|
|
|
|
|
|
|
3) Install dependencies:
|
|
|
|
|
2023-04-29 11:21:37 +00:00
|
|
|
<CodeBlock language="bash">{`\
|
2023-07-23 21:01:30 +00:00
|
|
|
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz next@13.4.12`}
|
2023-04-29 11:21:37 +00:00
|
|
|
</CodeBlock>
|
2023-01-15 03:36:13 +00:00
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
4) Download NextJS config scripts and place in the root folder:
|
|
|
|
|
|
|
|
- [`base64-loader.js`](pathname:///next/base64-loader.js)
|
|
|
|
- [`jsconfig.json`](pathname:///next/jsconfig.json)
|
|
|
|
- [`next.config.js`](pathname:///next/next.config.js)
|
|
|
|
- [`styles.css`](pathname:///next/styles.css)
|
|
|
|
|
|
|
|
On Linux or MacOS or WSL:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
curl -LO https://docs.sheetjs.com/next/base64-loader.js
|
|
|
|
curl -LO https://docs.sheetjs.com/next/jsconfig.json
|
|
|
|
curl -LO https://docs.sheetjs.com/next/next.config.js
|
|
|
|
curl -LO https://docs.sheetjs.com/next/styles.css
|
|
|
|
```
|
|
|
|
|
|
|
|
5) Download test scripts:
|
2023-01-15 03:36:13 +00:00
|
|
|
|
|
|
|
Download and place the following scripts in the `pages` subfolder:
|
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
- [`_app.js`](pathname:///next/_app.js)
|
2023-01-15 03:36:13 +00:00
|
|
|
- [`index.js`](pathname:///next/index.js)
|
|
|
|
- [`getServerSideProps.js`](pathname:///next/getServerSideProps.js)
|
|
|
|
- [`getStaticPaths.js`](pathname:///next/getStaticPaths.js)
|
|
|
|
- [`getStaticProps.js`](pathname:///next/getStaticProps.js)
|
|
|
|
|
|
|
|
Download [`[id].js`](pathname:///next/%5Bid%5D.js) and place in the
|
|
|
|
`pages/sheets` subfolder.
|
|
|
|
|
|
|
|
:::caution Percent-Encoding in the script name
|
|
|
|
|
|
|
|
The `[id].js` script must have the literal square brackets in the name. If your
|
|
|
|
browser saved the file to `%5Bid%5D.js`. rename the file.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
|
|
|
On Linux or MacOS or WSL:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
cd pages
|
2023-05-26 22:50:23 +00:00
|
|
|
curl -LO https://docs.sheetjs.com/next/_app.js
|
2023-01-15 03:36:13 +00:00
|
|
|
curl -LO https://docs.sheetjs.com/next/index.js
|
|
|
|
curl -LO https://docs.sheetjs.com/next/getServerSideProps.js
|
|
|
|
curl -LO https://docs.sheetjs.com/next/getStaticPaths.js
|
|
|
|
curl -LO https://docs.sheetjs.com/next/getStaticProps.js
|
|
|
|
cd sheets
|
|
|
|
curl -LOg 'https://docs.sheetjs.com/next/[id].js'
|
|
|
|
cd ../..
|
|
|
|
```
|
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
### Testing
|
|
|
|
|
|
|
|
6) Test the deployment:
|
2023-01-15 03:36:13 +00:00
|
|
|
|
|
|
|
```bash
|
2023-07-23 21:01:30 +00:00
|
|
|
npx next@13.4.12
|
2023-01-15 03:36:13 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
Open a web browser and access:
|
|
|
|
|
2023-05-03 03:40:40 +00:00
|
|
|
- `http://localhost:3000` landing page
|
|
|
|
- `http://localhost:3000/getStaticProps` shows data from the first sheet
|
|
|
|
- `http://localhost:3000/getServerSideProps` shows data from the first sheet
|
2023-05-26 22:50:23 +00:00
|
|
|
- `http://localhost:3000/getStaticPaths` shows a list (2 sheets)
|
2023-01-15 03:36:13 +00:00
|
|
|
|
|
|
|
The individual worksheets are available at
|
|
|
|
|
2023-05-03 03:40:40 +00:00
|
|
|
- `http://localhost:3000/sheets/0`
|
|
|
|
- `http://localhost:3000/sheets/1`
|
2023-01-15 03:36:13 +00:00
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
7) While the development server is running, open the `/getStaticProps` page and
|
|
|
|
open `sheetjs.xlsx` with a spreadsheet editor. In the editor, add a row to the
|
|
|
|
bottom of the "Indices" worksheet.
|
|
|
|
|
|
|
|
After saving the file, the website should refresh with the new row.
|
|
|
|
|
|
|
|
### Production Build
|
|
|
|
|
|
|
|
8) Stop the server and run a production build:
|
2023-01-15 03:36:13 +00:00
|
|
|
|
|
|
|
```bash
|
2023-07-23 21:01:30 +00:00
|
|
|
npx next@13.4.12 build
|
2023-01-15 03:36:13 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
The final output will show a list of the routes and types:
|
|
|
|
|
|
|
|
```
|
|
|
|
Route (pages) Size First Load JS
|
2023-07-23 21:01:30 +00:00
|
|
|
┌ ○ / 563 B 75.3 kB
|
|
|
|
├ /_app 0 B 74.8 kB
|
|
|
|
├ ○ /404 182 B 75 kB
|
|
|
|
├ λ /getServerSideProps 522 B 75.3 kB
|
|
|
|
├ ● /getStaticPaths 2.91 kB 77.7 kB
|
|
|
|
├ ● /getStaticProps 586 B 75.4 kB
|
|
|
|
└ ● /sheets/[id] (303 ms) 522 B 75.3 kB
|
2023-01-15 03:36:13 +00:00
|
|
|
├ /sheets/0
|
2023-05-26 22:50:23 +00:00
|
|
|
└ /sheets/1
|
2023-01-15 03:36:13 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
As explained in the summary, the `/getStaticPaths` and `/getStaticProps` routes
|
2023-05-26 22:50:23 +00:00
|
|
|
are completely static. 2 `/sheets/#` pages were generated, corresponding to 2
|
2023-01-15 03:36:13 +00:00
|
|
|
worksheets in the file. `/getServerSideProps` is server-rendered.
|
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
9) Try to build a static site:
|
2023-01-15 03:36:13 +00:00
|
|
|
|
|
|
|
```bash
|
2023-07-23 21:01:30 +00:00
|
|
|
npx next@13.4.12 export
|
2023-01-15 03:36:13 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
:::note The static export will fail!
|
|
|
|
|
|
|
|
A static page cannot be generated at this point because `/getServerSideProps`
|
|
|
|
is still server-rendered.
|
|
|
|
|
|
|
|
:::
|
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
### Static Site
|
|
|
|
|
|
|
|
10) Delete `pages/getServerSideProps.js` and rebuild:
|
2023-01-15 03:36:13 +00:00
|
|
|
|
|
|
|
```bash
|
|
|
|
rm -f pages/getServerSideProps.js
|
2023-07-23 21:01:30 +00:00
|
|
|
npx next@13.4.12 build
|
2023-01-15 03:36:13 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
Inspecting the output, there should be no lines with the `λ` symbol:
|
|
|
|
|
|
|
|
```
|
|
|
|
Route (pages) Size First Load JS
|
2023-07-23 21:01:30 +00:00
|
|
|
┌ ○ / 563 B 75.3 kB
|
|
|
|
├ /_app 0 B 74.8 kB
|
|
|
|
├ ○ /404 182 B 75 kB
|
|
|
|
├ ● /getStaticPaths 2.91 kB 77.7 kB
|
|
|
|
├ ● /getStaticProps 586 B 75.4 kB
|
|
|
|
└ ● /sheets/[id] 522 B 75.3 kB
|
2023-01-15 03:36:13 +00:00
|
|
|
├ /sheets/0
|
2023-05-26 22:50:23 +00:00
|
|
|
└ /sheets/1
|
2023-01-15 03:36:13 +00:00
|
|
|
```
|
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
11) Generate the static site:
|
2023-01-15 03:36:13 +00:00
|
|
|
|
|
|
|
```bash
|
2023-07-23 21:01:30 +00:00
|
|
|
npx next@13.4.12 export
|
2023-01-15 03:36:13 +00:00
|
|
|
```
|
|
|
|
|
2023-05-26 22:50:23 +00:00
|
|
|
The static site will be written to the `out` subfolder
|
|
|
|
|
|
|
|
12) Serve the static site:
|
2023-01-15 03:36:13 +00:00
|
|
|
|
|
|
|
```bash
|
|
|
|
npx http-server out
|
|
|
|
```
|
|
|
|
|
2023-07-23 21:01:30 +00:00
|
|
|
The command will start a local HTTP server at `http://localhost:8080/` for
|
|
|
|
testing the generated site. Note that `/getServerSideProps` will 404 since the
|
|
|
|
page was removed.
|