What is the proper way to use nuxt/icon with a SPA?
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 1.2k
- Forks
- 96
- Avg merge
- 8h 12m
- Merged PRs (30d)
- 2
Description
I am making a nuxt app where my deployment target is nuxt generate with ssr false on most pages, and true on like /pricing and seo pages. Pocketbase will serve my static files.
In nuxt dev, icons wont load, but i found a working solution I want to verify this is OK and im not bloating my bundle, or creating future issues.
nuxt.config.ts
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
compatibilityDate: '2026-05-21',
modules: [
'@nuxt/eslint',
'@nuxt/ui'
],
devtools: {
enabled: true
},
css: ['~/assets/css/main.css'],
// PocketBase SDK base URL — consumed by `plugins/pocketbase.ts`.
// In production the Nuxt static files are served by PocketBase on the same
// origin, so '/' works as a relative base URL (PB SDK strips the trailing
// slash and builds relative paths like /api/...). The devProxy above handles
// forwarding in dev. Nuxt automatically overrides this from the
// `NUXT_PUBLIC_PB_URL` env var at runtime.
runtimeConfig: {
public: {
pbUrl: '/'
}
},
// SPA mode for all authenticated/dynamic app routes.
// The marketing pages (/ and /pricing) remain SSR-pre-rendered via @nuxt/content.
// PocketBase serves the static output and handles SPA fallback via apis.Static with
// indexFallback=true (see main.go OnServe hook).
routeRules: {
'/auth/**': { ssr: false },
'/portal/**': { ssr: false },
'/book/**': { ssr: false },
'/forms/**': { ssr: false }
},
// Dev-only: proxy PocketBase API and admin routes to avoid CORS issues
// (browser fetches to /api/* and /_/* are forwarded to PB on :8090)
nitro: {
devProxy: {
'/api': { target: 'http://localhost:8090/api', changeOrigin: true },
'/_/': { target: 'http://localhost:8090/_/', changeOrigin: true }
},
prerender: {
// Don't fail the build if a dynamic route (e.g. /book/[slug]) can't be crawled
failOnError: false
}
},
eslint: {
config: {
stylistic: {
commaDangle: 'never',
braceStyle: '1tbs'
}
}
},
// @nuxt/icon: In production, PocketBase serves static output with no Nitro
// server, so the /_nuxt_icon server endpoint is unreachable from SPA pages.
// Use provider: 'none' + client bundle to pre-bundle all icons at build time.
//
// - icons: explicit list of every icon hardcoded in @nuxt/ui's theme defaults
// (from node_modules/@nuxt/ui/dist/shared/ui.*.mjs). More reliable than
// scanning node_modules since those files are hashed and not .vue files.
// - scan: true picks up any icon literals in your own app/** source files.
icon: {
provider: 'none',
clientBundle: {
scan: true,
icons: [
'lucide:arrow-down',
'lucide:arrow-left',
'lucide:arrow-right',
'lucide:arrow-up',
'lucide:arrow-up-right',
'lucide:check',
'lucide:chevron-down',
'lucide:chevron-left',
'lucide:chevron-right',
'lucide:chevrons-left',
'lucide:chevrons-right',
'lucide:chevron-up',
'lucide:circle-alert',
'lucide:circle-check',
'lucide:circle-x',
'lucide:copy',
'lucide:copy-check',
'lucide:ellipsis',
'lucide:eye',
'lucide:eye-off',
'lucide:file',
'lucide:folder',
'lucide:folder-open',
'lucide:grip-vertical',
'lucide:hash',
'lucide:info',
'lucide:lightbulb',
'lucide:loader-circle',
'lucide:menu',
'lucide:minus',
'lucide:monitor',
'lucide:moon',
'lucide:panel-left-close',
'lucide:panel-left-open',
'lucide:plus',
'lucide:rotate-ccw',
'lucide:search',
'lucide:square',
'lucide:sun',
'lucide:terminal',
'lucide:triangle-alert',
'lucide:upload',
'lucide:x'
]
}
}
})
app.config.ts
export default defineAppConfig({
ui: {
colors: {
primary: 'violet',
neutral: 'slate'
},
}
})
package.json
{
"name": "frontend",
"private": true,
"type": "module",
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare",
"lint": "eslint .",
"typecheck": "nuxt typecheck"
},
"dependencies": {
"@iconify-json/lucide": "^1.2.108",
"@iconify-json/simple-icons": "^1.2.83",
"@nuxt/ui": "^4.7.1",
"nuxt": "^4.4.6",
"pocketbase": "^0.26.2",
"tailwindcss": "^4.3.0"
},
"devDependencies": {
"@nuxt/eslint": "^1.15.2",
"eslint": "^10.4.0",
"typescript": "^6.0.3",
"vue-tsc": "^3.3.0"
},
"packageManager": "pnpm@11.1.3"
}
I hate listing every single icon out. I wish I could just drop in nuxt/ui (comes with nuxt/icon), have defaults in my config and be on my happy way.
Did I configure this right?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with nuxt.config.ts and the @nuxt/icon clientBundle configuration shown in the issue. Compare behavior under nuxt dev and nuxt generate with the documented SPA and SSR modes, then verify whether the proposed provider and icon list work without unnecessary bundle growth. Done means the supported configuration and its tradeoffs are clear.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- nuxt, typescript
- Domain
- build-system, frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100