--- title: NuxtJS pagination_prev: demos/net/index pagination_next: demos/mobile/index --- import current from '/version.js'; import CodeBlock from '@theme/CodeBlock'; `@nuxt/content` is a file-based CMS for Nuxt, enabling static-site generation and on-demand server rendering powered by spreadsheets. :::note The following deployments were tested: | Nuxt Content | Nuxt | Date | |:-------------|:---------|:-----------| | `1.15.1` | `2.16.3` | 2023-04-06 | | `2.3.0` | `3.0.0` | 2023-01-19 | ::: ## Nuxt Content v1 Nuxt Content v1 is designed to work with Nuxt v2. ### Configuration Through an override in `nuxt.config.js`, Nuxt Content will use custom parsers. Differences from a stock `create-nuxt-app` config are shown below: ```js title="nuxt.config.js" import { readFile, utils } from 'xlsx'; // This will be called when the files change const parseSheet = (file, { path }) => { // `path` is a path that can be read with `XLSX.readFile` const wb = readFile(path); const o = wb.SheetNames.map(name => ({ name, data: utils.sheet_to_json(wb.Sheets[name])})); return { data: o }; } export default { // ... // content.extendParser allows us to hook into the parsing step content: { extendParser: { // the keys are the extensions that will be matched. The "." is required ".numbers": parseSheet, ".xlsx": parseSheet, ".xls": parseSheet, // can add other extensions like ".fods" as desired } }, // ... } ``` ### Template Use When a spreadsheet is placed in the `content` folder, Nuxt will find it. The data can be referenced in a view with `asyncData`. The name should not include the extension, so `"sheetjs.numbers"` would be referenced as `"sheetjs"`: ```js async asyncData ({$content}) { return { // $content('sheetjs') will match files with extensions in nuxt.config.js data: await $content('sheetjs').fetch() }; } ``` In the template, `data.data` is an array of objects. Each object has a `name` property for the worksheet name and a `data` array of row objects. This maps neatly with nested `v-for`: ```xml
{{ row.Name }} {{ row.Index }}
``` ### Nuxt Content Demo 1) Create a stock app: ```bash npx create-nuxt-app@4.0.0 SheetJSNuxt ``` When prompted, enter the following options: - `Project name`: press Enter (use default `SheetJSNuxt`) - `Programming language`: press Down Arrow (`TypeScript` selected) then Enter - `Package manager`: select `Npm` and press Enter - `UI framework`: select `None` and press Enter - `Nuxt.js modules`: scroll to `Content`, select with Space, then press Enter - `Linting tools`: press Enter (do not select any Linting tools) - `Testing framework`: select `None` and press Enter - `Rendering mode`: select `Universal (SSR / SSG)` and press Enter - `Deployment target`: select `Static (Static/Jamstack hosting)` and press Enter - `Development tools`: press Enter (do not select any Development tools) - `What is your GitHub username?`: press Enter - `Version control system`: select `None` The project will be configured and modules will be installed. 2) Install the SheetJS library and start the server: {`\ cd SheetJSNuxt npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz npm run dev`} When the build finishes, the terminal will display a URL like: ``` ℹ Listening on: http://localhost:64688/ ``` The server is listening on that URL. Open the link in a web browser. 3) Download and move to the `content` folder. ```bash curl -L -o content/pres.xlsx https://sheetjs.com/pres.xlsx ``` 4) Modify `nuxt.config.js` as follows: - Add the following to the top of the script: ```js import { readFile, utils } from 'xlsx'; // This will be called when the files change const parseSheet = (file, { path }) => { // `path` is a path that can be read with `XLSX.readFile` const wb = readFile(path); const o = wb.SheetNames.map(name => ({ name, data: utils.sheet_to_json(wb.Sheets[name])})); return { data: o }; } ``` - Look for the exported object. There should be a `content` property: ```js // Content module configuration: https://go.nuxtjs.dev/config-content content: {}, ``` Replace the property with the following definition: ```js // content.extendParser allows us to hook into the parsing step content: { extendParser: { // the keys are the extensions that will be matched. The "." is required ".numbers": parseSheet, ".xlsx": parseSheet, ".xls": parseSheet, // can add other extensions like ".fods" as desired } }, ``` (If the property is missing, add it to the end of the exported object) 5) Replace `pages/index.vue` with the following: ```html ``` The browser should refresh to show the contents of the spreadsheet. If it does not, click Refresh manually or open a new browser window. ![Nuxt Demo end of step 5](pathname:///nuxt/nuxt5.png) 6) To verify that hot loading works, open `pres.xlsx` from the `content` folder in Excel. Add a new row to the bottom and save the file: ![Adding a new line to `pres.xlsx`](pathname:///nuxt/nuxl6.png) The server terminal window should show a line like: ``` ℹ Updated ./content/pres.xlsx @nuxt/content 05:43:37 ``` The page should automatically refresh with the new content: ![Nuxt Demo end of step 6](pathname:///nuxt/nuxt6.png) 7) Stop the server (press `CTRL+C` in the terminal window) and run ```bash npm run generate ``` This will create a static site in the `dist` folder, which can be served with: ```bash npx http-server dist ``` Accessing the page `http://localhost:8080` will show the page contents. Verifying the static nature is trivial: make another change in Excel and save. The page will not change. ## Nuxt Content v2 Nuxt Content v2 is designed to work with Nuxt v3. ### Overview Nuxt Content `v2` supports custom transformers for controlling data. Although the library hard-codes UTF-8 interpretations, the `_id` field currently uses the pattern `content:` followed by the filename (if files are placed in the `content` folder directly). This enables a transformer to re-read the file: ```ts import { defineTransformer } from "@nuxt/content/transformers/utils"; import { read, utils } from "xlsx"; import { readFileSync } from "node:fs"; import { resolve } from 'node:path'; export default defineTransformer({ name: 'sheetformer', extensions: ['.xlsx'], parse (_id: string, rawContent: string) { const wb = read(readFileSync(resolve("./content/" + _id.slice(8)))); const body = wb.SheetNames.map(name => ({ name, data: utils.sheet_to_json(wb.Sheets[name])})); return { _id, body }; } }); ``` Pages can pull data using `useAsyncData`: ```html ``` Pages should use `ContentRenderer` to reference the data: ```html ``` ### Nuxt Content 2 Demo 1) Create a stock app and install dependencies: ```bash npx nuxi init -t content sheetjs-nc2 cd sheetjs-nc2 npx yarn install npx yarn add --dev @types/node ``` 2) Install the SheetJS library and start the server: {`\ npx yarn add https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz npx yarn dev`} When the build finishes, the terminal will display a URL like: ``` > Local: http://localhost:3000/ ``` The server is listening on that URL. Open the link in a web browser. 3) Download and move to the `content` folder. ```bash curl -L -o content/pres.xlsx https://sheetjs.com/pres.xlsx ``` 4) Create the transformer. Two files must be written: - `sheetformer.ts` (the raw transformer module): ```ts title="sheetformer.ts" // @ts-ignore import { defineTransformer } from "@nuxt/content/transformers/utils"; import { read, utils } from "xlsx"; import { readFileSync } from "node:fs"; import { resolve } from 'node:path'; export default defineTransformer({ name: 'sheetformer', extensions: ['.xlsx'], parse (_id: string, rawContent: string) { const wb = read(readFileSync(resolve("./content/" + _id.slice(8)))); const body = wb.SheetNames.map(name => ({ name, data: utils.sheet_to_json(wb.Sheets[name])})); return { _id, body }; } }); ``` - `sheetmodule.ts` (the Nuxt configuration module): ```ts title="sheetmodule.ts" import { resolve } from 'path' import { defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup (_options, nuxt) { nuxt.options.nitro.externals = nuxt.options.nitro.externals || {} nuxt.options.nitro.externals.inline = nuxt.options.nitro.externals.inline || [] nuxt.options.nitro.externals.inline.push(resolve('./sheetmodule')) // @ts-ignore nuxt.hook('content:context', (contentContext) => { contentContext.transformers.push(resolve('./sheetformer.ts')) }) } }) ``` After creating the source files, the module must be added to `nuxt.config.ts`: ```ts title="nuxt.config.ts" import SheetJSModule from './sheetmodule' export default defineNuxtConfig({ modules: [ SheetJSModule, '@nuxt/content' ], content: {} }) ``` Restart the dev server by exiting the process (Control+C) and running: ```bash npx nuxi clean npx nuxi typecheck npx yarn run dev ``` Loading `http://localhost:3000/pres` should show some JSON data: ```json { // ... "data": { "_path": "/pres", // ... "_id": "content:pres.xlsx", "body": [ { "name": "Sheet1", // <-- sheet name "data": [ // <-- array of data objects { "Name": "Bill Clinton", "Index": 42 }, ``` 5) Create a page. Save the following content to `pages/pres.vue`: ```html title="pages/pres.vue" ``` Restart the dev server by exiting the process (Control+C) and running: ```bash npx nuxi clean npx yarn run dev ``` The browser should now display an HTML table. 6) To verify that hot loading works, open `pres.xlsx` from the `content` folder in Excel. Add a new row to the bottom and save the file. The page should automatically refresh with the new content. 7) Stop the server (press `CTRL+C` in the terminal window) and run ```bash npx yarn run generate ``` This will create a static site in `.output/public`, which can be served with: ```bash npx http-server .output/public ``` Accessing `http://localhost:8080/pres` will show the page contents. Verifying the static nature is trivial: make another change in Excel and save. The page will not change.