# Using Cesium npm package with Nuxt 3

**URL:** <https://community.cesium.com/t/using-cesium-npm-package-with-nuxt-3/29593>\
**Category:** CesiumJS\
**Created:** [January 28, 2024, 2:29pm UTC](https://community.cesium.com/t/using-cesium-npm-package-with-nuxt-3/29593 "2024-01-28T14:29:18Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![Toby\_Smith](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/toby_smith/32/16296_2.png) [@Toby\_Smith](https://community.cesium.com/u/Toby_Smith)\
**Post date:** [January 28, 2024, 2:29pm UTC](https://community.cesium.com/t/using-cesium-npm-package-with-nuxt-3/29593/1 "2024-01-28T14:29:18Z")

</div>

Hi all. I’ve searched high and low for an answer to this, but it seems like most folks are relying on the (awesome) vue-cesium package these days.

I want to use CesiumJS from an npm package with my Nuxt 3 app, but it seems like Cesium isn’t finding its assets in node\_modules. Here’s my trivial `CesiumViewer.vue` component:

```auto
<template>
  <div id="cesiumContainer"></div>
</template>

<script setup lang="ts">
import { ImageryLayer, Ion, OpenStreetMapImageryProvider, Terrain, Viewer } from "cesium";
import "cesium/Build/Cesium/Widgets/widgets.css";
</script>

<script lang="ts">
export default {
  mounted() {
    Ion.defaultAccessToken = '<my Cesium Ion token>'

    let viewer = new Viewer("cesiumContainer", {
      terrain: Terrain.fromWorldTerrain(),
      baseLayerPicker: false,
      animation: true,
      shouldAnimate: true,
      baseLayer: new ImageryLayer(new OpenStreetMapImageryProvider({
        url: "https://tile.openstreetmap.org/"
      })),
    });
  }
}
</script>

```

My `nuxt.config.js` is completely vanilla:

```auto
export default defineNuxtConfig({
    ssr: false,
    app: {
      baseURL: '/',
    },
})

```

And my `package.json` is as well:

```auto
{
    "name": "nuxt3-cesium-test",
    "private": true,
    "type": "module",
    "scripts": {
        "dev": "nuxt dev"
    },
    "devDependencies": {
        "nuxt": "^3.8.2",
        "vue": "^3.3.10",
        "vue-router": "^4.2.5"
    },
    "dependencies": {
        "cesium": "^1.113.0"
    }
}

```

When I try to run this the Cesium UI is displayed, but there’s no globe. And I get these errors in the console:

```auto
VM2444:1 Uncaught (in promise) SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
    at JSON.parse (<anonymous>)
    at cesium.js?v=641800c9:15846:17
(anonymous) @ cesium.js?v=641800c9:15846
Promise.then (async)
ApproximateTerrainHeights.initialize @ cesium.js?v=641800c9:16605
GroundPrimitive.initializeTerrainHeights @ cesium.js?v=641800c9:123416
DataSourceDisplay @ cesium.js?v=641800c9:202019
Viewer @ cesium.js?v=641800c9:245759
mounted @ CesiumViewer.vue:16
(anonymous) @ vue.js?v=8c90a48a:4320
callWithErrorHandling @ vue.js?v=8c90a48a:1652
callWithAsyncErrorHandling @ vue.js?v=8c90a48a:1660
hook. __weh.hook.__ weh @ vue.js?v=8c90a48a:4300
flushPostFlushCbs @ vue.js?v=8c90a48a:1827
render2 @ vue.js?v=8c90a48a:8107
mount @ vue.js?v=8c90a48a:5368
app.mount @ vue.js?v=8c90a48a:11059
initApp @ entry.js:54
await in initApp (async)
(anonymous) @ entry.js:64

```

and…

```auto
localhost/:1 Uncaught (in promise) DOMException: The source image could not be decoded.
Promise.then (async)
SkyBox.update @ cesium.js?v=641800c9:119354
Scene.updateEnvironment @ cesium.js?v=641800c9:118367
render @ cesium.js?v=641800c9:118774
tryAndCatchError @ cesium.js?v=641800c9:118789
Scene.render @ cesium.js?v=641800c9:118841
CesiumWidget.render @ cesium.js?v=641800c9:120241
render2 @ cesium.js?v=641800c9:119664
requestAnimationFrame (async)
startRenderLoop @ cesium.js?v=641800c9:119688
set @ cesium.js?v=641800c9:120080
CesiumWidget @ cesium.js?v=641800c9:119877
Viewer @ cesium.js?v=641800c9:245726
mounted @ CesiumViewer.vue:16
(anonymous) @ vue.js?v=8c90a48a:4320
callWithErrorHandling @ vue.js?v=8c90a48a:1652
callWithAsyncErrorHandling @ vue.js?v=8c90a48a:1660
hook. __weh.hook.__ weh @ vue.js?v=8c90a48a:4300
flushPostFlushCbs @ vue.js?v=8c90a48a:1827
render2 @ vue.js?v=8c90a48a:8107
mount @ vue.js?v=8c90a48a:5368
app.mount @ vue.js?v=8c90a48a:11059
initApp @ entry.js:54
await in initApp (async)
(anonymous) @ entry.js:64

```

I’m sure I’m missing something obvious here as far as pointing Cesium at its assets, but I’m completely missing it.

Thanks so much for any help!

---

<div class="post-metadata">

**Author:** ![Toby\_Smith](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/toby_smith/32/16296_2.png) [@Toby\_Smith](https://community.cesium.com/u/Toby_Smith)\
**Post date:** [January 29, 2024, 11:53am UTC](https://community.cesium.com/t/using-cesium-npm-package-with-nuxt-3/29593/2 "2024-01-29T11:53:47Z")

</div>

@Jacques27, it seems that you’ve been down this road. Were you trying to do the same thing? It looks like you made it further than I am.

---

<div class="post-metadata">

**Author:** ![Jacques27](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/jacques27/32/11887_2.png) [@Jacques27](https://community.cesium.com/u/Jacques27)\
**Post date:** [January 30, 2024, 12:57am UTC](https://community.cesium.com/t/using-cesium-npm-package-with-nuxt-3/29593/3 "2024-01-30T00:57:52Z")

</div>

Hi Toby,

I have indeed and it’s a bit tedious to be honest and it was a lot of trial and error at first but I’ve built multiple projects with it already.

One thing you should probably add is a script to initialise the base URL instead of adding it to the nuxt.config.

> export default function setupCesiumBaseUrl() {  
> const cesiumBaseUrl = ‘./lib/cesium’;  
> window.CESIUM\_BASE\_URL = cesiumBaseUrl;  
> }

I’m calling this function right after imports in my app.vue.

I have also added a script that copies the necessary folders from the node\_modules when you build your app (requires packages “del” and “recursive-copy” to be installed:

> import copy from ‘recursive-copy’;  
> import { deleteSync } from ‘del’;
> 
> const baseDir = ‘node\_modules/cesium/Build/CesiumUnminified’;
> 
> const targets = [‘Assets/ **/\*', 'ThirdParty/** /_', 'Widgets/\*\*/_’, ‘Workers/\*\*/\*’, ‘Cesium.js’];
> 
> deleteSync(targets.map((src) =\> `.output/public/lib/cesium/${src}`));
> 
> copy(baseDir, ‘.output/public/lib/cesium’, {  
> expand: true,  
> overwrite: true,  
> filter: targets,  
> });

In order to get it running in dev mode instead of building it every time, I’ve also added a “lib” folder at the project root which includes the same as what the script above copies. Below is what my folder structure looks like.

 ![image](https://global.discourse-cdn.com/cesium/original/3X/e/3/e3c071fa6032660af2fce40bb1dfb542de568bd5.png)

Note: My latest cesium project is running with nuxt 3.6.5 and I have not tried this with newer versions of nuxt yet.

Sorry for the short answer, not much time currently, hope it helps.

Best,  
Jacques

---

<div class="post-metadata">

**Author:** ![Toby\_Smith](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/toby_smith/32/16296_2.png) [@Toby\_Smith](https://community.cesium.com/u/Toby_Smith)\
**Post date:** [January 30, 2024, 11:29am UTC](https://community.cesium.com/t/using-cesium-npm-package-with-nuxt-3/29593/4 "2024-01-30T11:29:38Z")

</div>

Thank you @Jacques27! That does indeed seem pretty tedious, but I’ll give it a try.

---

<div class="post-metadata">

**Author:** ![Kevin\_Kibler](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/kevin_kibler/32/24330_2.png) [@Kevin\_Kibler](https://community.cesium.com/u/Kevin_Kibler)\
**Post date:** [June 20, 2024, 6:36pm UTC](https://community.cesium.com/t/using-cesium-npm-package-with-nuxt-3/29593/5 "2024-06-20T18:36:59Z")

</div>

I managed to get this working without copying files around. You can configure your server to serve the Cesium resources from the npm package directory directly. This section of the [Cesium quick start guide](https://cesium.com/learn/cesiumjs-learn/cesiumjs-quickstart/#install-with-npm) has additional details about what needs to be served. Using Nuxt+Vue defaults, this means configuring Nitro to serve the files from the node\_modules directory and configuring Cesium to use the right base url to find them:

`nuxt.config.ts`:

```auto
export default defineNuxtConfig({
    app: {
      head: {
        script: [
          {
            //must match the nitro config below for where the files are being served publicly
            children: `window.CESIUM_BASE_URL='_nuxt/Cesium';`,
          },
        ],
      }
    },
    nitro: {
      publicAssets: [ {
        //Nuxt will copy the files here and serve them publicly
        baseURL: '_nuxt/Cesium/Assets',
        dir: '../node_modules/cesium/Build/Cesium/Assets'
      },
      {
        baseURL: '_nuxt/Cesium/Workers',
        dir: '../node_modules/cesium/Build/Cesium/Workers'
      },
      {
        baseURL: '_nuxt/Cesium/ThirdParty',
        dir: '../node_modules/cesium/Build/Cesium/ThirdParty'
      },
      {
        baseURL: '_nuxt/Cesium/Widgets',
        dir: '../node_modules/cesium/Build/Cesium/Widgets'
      },
    ]},
})

```

`component.vue`:

```auto
import * as Cesium from 'cesium'
import "cesium/Build/Cesium/Widgets/widgets.css";
...

```

Additionally, I had a problem where using the full cesium package in workers would crash the workers due to an issue with Cesium’s knockout dependency. This happens even though I’m not using anything from the widget package in the worker, but only happens in dev mode (`nuxt dev`), not a production build (`nuxt build && nuxt start`). So I used the `@cesium/engine` package directly in those cases instead. Including both the `cesium` and `@cesium/engine` packages isn’t ideal, but I couldn’t come up with a better solution.

`worker.js`:

```auto
import { SampledPositionProperty } from '@cesium/engine'
...

```

---

<div class="post-metadata">

**Author:** ![tong\_TQ](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/tong_tq/32/13626_2.png) [@tong\_TQ](https://community.cesium.com/u/tong_TQ)\
**Post date:** [December 13, 2024, 2:35pm UTC](https://community.cesium.com/t/using-cesium-npm-package-with-nuxt-3/29593/7 "2024-12-13T14:35:55Z")

</div>

Hi Toby, I use resource code([GitHub - nshen/vite-plugin-cesium: ⚡ Vite plugin for Cesium](https://github.com/nshen/vite-plugin-cesium.git)) when I look this problem, it’s work.

this is my config

import { type Plugin, type UserConfig, normalizePath, type HtmlTagDescriptor } from ‘vite’

import path from ‘path’

import fs from ‘fs-extra’

import serveStatic from ‘serve-static’

import externalGlobals from ‘rollup-plugin-external-globals’

interface VitePluginCesiumOptions {  
/\*\*

- rebuild cesium library, default: false  
\*/  
rebuildCesium?: boolean  
devMinifyCesium?: boolean  
cesiumBuildRootPath?: string  
cesiumBuildPath?: string  
cesiumBaseUrl?: string  
}

function vitePluginCesium(options: VitePluginCesiumOptions = {}): Plugin {  
const {  
rebuildCesium = false,  
devMinifyCesium = false,  
cesiumBuildRootPath = ‘node\_modules/cesium/Build’,  
cesiumBuildPath = ‘node\_modules/cesium/Build/Cesium/’,  
cesiumBaseUrl = ‘cesium/’  
} = options

let CESIUM\_BASE\_URL = cesiumBaseUrl  
if (!CESIUM\_BASE\_URL.endsWith(‘/’)) {  
CESIUM\_BASE\_URL += ‘/’  
}  
let outDir = ‘dist’  
let base: string = ‘/’  
let isBuild: boolean = false

return {  
name: ‘vite-plugin-cesium’,

```
config(c, { command }) {
  isBuild = command === 'build'
  if (c.base !== undefined) {
    base = c.base
    if (base === '') base = './'
  }
  if (c.build?.outDir) {
    outDir = c.build.outDir
  }
  CESIUM_BASE_URL = path.posix.join(base, CESIUM_BASE_URL)
  const userConfig: UserConfig = {}
  if (!isBuild) {
    // -----------dev-----------
    userConfig.define = {
      CESIUM_BASE_URL: JSON.stringify(CESIUM_BASE_URL)
    }
  } else {
    // -----------build------------
    if (rebuildCesium) {
      // build 1) rebuild cesium library
      userConfig.build = {
        assetsInlineLimit: 0,
        chunkSizeWarningLimit: 5000,
        rollupOptions: {
          output: {
            intro: `window.CESIUM_BASE_URL = ${JSON.stringify(CESIUM_BASE_URL)};`
          }
        }
      }
    } else {
      // build 2) copy Cesium.js later
      userConfig.build = {
        rollupOptions: {
          external: ['cesium'],
          plugins: [externalGlobals({ cesium: 'Cesium' })]
        }
      }
    }
  }
  return userConfig
},

configureServer({ middlewares }) {
  const cesiumPath = path.join(
    cesiumBuildRootPath,
    devMinifyCesium ? 'Cesium' : 'CesiumUnminified'
  )
  middlewares.use(
    path.posix.join('/', CESIUM_BASE_URL),
    serveStatic(cesiumPath, {
      setHeaders: (res, path, stat) => {
        res.setHeader('Access-Control-Allow-Origin', '*')
      }
    })
  )
},

async closeBundle() {
  if (isBuild) {
    try {
      await fs.copy(
        path.join(cesiumBuildPath, 'Assets'),
        path.join(outDir, CESIUM_BASE_URL, 'Assets')
      )
      await fs.copy(
        path.join(cesiumBuildPath, 'ThirdParty'),
        path.join(outDir, CESIUM_BASE_URL, 'ThirdParty')
      )
      await fs.copy(
        path.join(cesiumBuildPath, 'Workers'),
        path.join(outDir, CESIUM_BASE_URL, 'Workers')
      )
      await fs.copy(
        path.join(cesiumBuildPath, 'Widgets'),
        path.join(outDir, CESIUM_BASE_URL, 'Widgets')
      )
      if (!rebuildCesium) {
        await fs.copy(
          path.join(cesiumBuildPath, 'Cesium.js'),
          path.join(outDir, CESIUM_BASE_URL, 'Cesium.js')
        )
      }
    } catch (err) {
      console.error('copy failed', err)
    }
  }
},

transformIndexHtml() {
  const tags: HtmlTagDescriptor[] = [
    {
      tag: 'link',
      attrs: {
        rel: 'stylesheet',
        href: normalizePath(path.join(CESIUM_BASE_URL, 'Widgets/widgets.css'))
      }
    }
  ]
  if (isBuild && !rebuildCesium) {
    tags.push({
      tag: 'script',
      attrs: {
        src: normalizePath(path.join(CESIUM_BASE_URL, 'Cesium.js'))
      }
    })
  }
  return tags
}

```

}  
}

 ![企业微信截图_17340544787907](https://global.discourse-cdn.com/cesium/original/3X/a/f/afb3f3235b44d553cee5ceef2b402704fad18c7d.png)
