@jaiswald159/shared-settings
v2.0.4
Published
A lightweight shared settings library using [Pinia](https://pinia.vuejs.org/) for state management, built for Vue 3 / Nuxt 3 applications. Provides a centralized store to manage application settings such as language preferences, themes, and site-specific
Readme
@jaiswald159/shared-settings
A lightweight shared settings library using Pinia for state management, built for Vue 3 / Nuxt 3 applications. Provides a centralized store to manage application settings such as language preferences, themes, and site-specific configurations.
š¦ Installation
Install the package using npm:
npm install @jaiswald159/shared-settingsš Basic Usage
Import and use the store in your Vue or Nuxt application:
import { useSettingsStore } from '@jaiswald159/shared-settings';
// Example of a dummy app store that provides application data
const myAppStore = () => ({
getApplicationData: (key: string) => {
if (key === 'loggedSiteID') {
return 'site-abc-123'; // Replace with dynamic site ID
}
return null;
}
});
// Initialize the settings store
const settingsStore = useSettingsStore(myAppStore);
// Update settings
settingsStore.UPDATE_SETTINGS({
siteID: 'site-abc-123',
theme: 'dark',
notifications: true
});
// Get settings for the current site
const currentSettings = settingsStore.GET_SETTINGS();šÆ Complete Example: Nuxt 3 Implementation
Here's a complete example of how to use the library in a Nuxt 3 application:
1. Setup Dependencies
{
"dependencies": {
"@jaiswald159/shared-settings": "^1.0.2",
"@pinia/nuxt": "^0.11.2",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1"
}
}2. Nuxt Configuration (nuxt.config.ts)
export default defineNuxtConfig({
modules: ['@pinia/nuxt'],
pinia: {
autoImports: ['defineStore', 'storeToRefs']
}
})3. Pinia Plugin (plugins/pinia.client.ts)
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
export default defineNuxtPlugin((nuxtApp) => {
const pinia = nuxtApp.$pinia
if (pinia) {
pinia.use(piniaPluginPersistedstate)
}
})4. App Store Helper
// composables/useAppStore.ts
export const useAppStore = () => ({
getApplicationData: (key: string) => {
if (key === 'loggedSiteID') return 'SITE123'
return null
}
})5. Main Component (pages/index.vue)
<template>
<div>
<h2>Settings Management</h2>
<div v-if="error" class="error">
<p>Error: {{ error }}</p>
<button @click="retry">Retry</button>
</div>
<div v-else>
<pre>{{ currentSettings }}</pre>
<button @click="saveSettings">Save Settings</button>
<button @click="loadSettings">Load Settings</button>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref, onMounted } from 'vue'
import { useSettingsStore } from '@jaiswald159/shared-settings'
// App store helper
const useAppStore = () => ({
getApplicationData: (key: string) => {
if (key === 'loggedSiteID') return 'SITE123'
return null
}
})
const error = ref<string | null>(null)
const store = ref<any>(null)
const retry = async () => {
error.value = null
await initializeStore()
}
const initializeStore = async () => {
try {
if (process.client) {
// Wait for the app to be fully ready
await new Promise(resolve => {
if (document.readyState === 'complete') {
resolve(undefined)
} else {
window.addEventListener('load', resolve)
}
})
// Initialize the settings store
store.value = useSettingsStore(useAppStore)
}
} catch (err) {
console.error('Failed to load shared-settings:', err)
error.value = 'Failed to load settings library.'
}
}
const currentSettings = computed(() => {
if (!store.value) return 'Loading...'
try {
return store.value.GET_SETTINGS()
} catch (err) {
console.error('Error getting settings:', err)
return 'Error loading settings'
}
})
const saveSettings = async () => {
if (!store.value) {
error.value = 'Store not initialized'
return
}
try {
store.value.UPDATE_SETTINGS({
siteID: 'SITE123',
language: 'fr',
darkMode: true,
theme: 'dark',
notifications: true
})
console.log('Settings saved successfully!')
} catch (err) {
console.error('Error saving settings:', err)
error.value = 'Failed to save settings'
}
}
const loadSettings = () => {
if (!store.value) {
error.value = 'Store not initialized'
return
}
try {
const settings = store.value.GET_SETTINGS()
console.log('Current settings:', settings)
} catch (err) {
console.error('Error loading settings:', err)
error.value = 'Failed to load settings'
}
}
onMounted(() => {
initializeStore()
})
</script>
<style scoped>
.error {
color: red;
padding: 1rem;
border: 1px solid red;
border-radius: 4px;
margin: 1rem 0;
}
button {
padding: 0.5rem 1rem;
margin: 0.5rem;
border: 1px solid #ccc;
border-radius: 4px;
background: #f0f0f0;
cursor: pointer;
}
button:hover {
background: #e0e0e0;
}
pre {
background: #f5f5f5;
padding: 1rem;
border-radius: 4px;
overflow-x: auto;
}
</style>6. Advanced Usage with Vue Persistence
For enhanced Vue integration, you can also create a custom implementation using @vueuse/core:
// composables/useCustomSettingsStore.ts
import { ref, computed } from 'vue'
import { useStorage } from '@vueuse/core'
interface Setting {
siteID: string
[key: string]: any
}
interface AppStore {
getApplicationData: (key: string) => any
}
export const useCustomSettingsStore = (useStore?: () => AppStore) => {
const siteID = computed(() => (useStore ? useStore().getApplicationData('loggedSiteID') : null))
// Use Vue's persistence state instead of localStorage
const storage = typeof window !== 'undefined' ? localStorage : undefined
const settings = useStorage<Setting[]>('activityAttendanceSettings', [], storage)
const UPDATE_SETTINGS = (data: Setting) => {
if (!data.siteID) throw new Error('siteID is required')
if (settings.value.length > 0) {
const index = settings.value.findIndex((item: Setting) => item.siteID === data.siteID)
if (index !== -1) {
settings.value[index] = data
} else {
settings.value.push(data)
}
} else {
settings.value.push(data)
}
}
const GET_SETTINGS = () => {
const findSetting = settings.value.find((item: Setting) => item.siteID === siteID.value)
return findSetting || false
}
return {
settings,
UPDATE_SETTINGS,
GET_SETTINGS,
}
}š File Structure
shared-settings/
āāā dist/ # Compiled output files
āāā useSettingsStore.ts # Pinia store definition for shared settings
āāā vite.config.ts # Vite build configuration
āāā tsconfig.json # TypeScript configuration
āāā index.ts # Entry file
āāā package.json # NPM metadata
āāā README.md # Documentation fileāļø package.json (CSS integration)
Below is the current package.json from this repository and a short explanation of how CSS is included and published with the package.
{
"name": "@jaiswald159/shared-settings",
"version": "2.0.1",
"main": "dist/index.js",
"type": "module",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs",
"style": "./dist/global.css"
},
"./dist/global.css": "./dist/global.css",
"./global.css": "./global.css"
},
"scripts": {
"build": "vite build",
"dev": "vite",
"postbuild": "powershell -Command \"if (Test-Path dist) { Copy-Item -Path global.css -Destination dist -Force }\""
},
"files": [
"dist",
"global.css"
],
"style": "global.css",
"dependencies": {
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.0",
"vue": "^3.3.4"
}
}Why this matters and how CSS is published:
exports["."].styleand the top-levelstylefield point to the published CSS so bundlers that respect thestylefield can auto-detect the package stylesheet.- The
postbuildscript ensuresglobal.cssis copied intodist/aftervite buildso the published package contains the CSS alongside compiled JS. - The
filesarray explicitly includesdistandglobal.cssin the package tarball.
How to consume the CSS in host apps
- Automatic (if your bundler supports
style): importing the package can make bundlers pick up thestylefield. Example:import '@jaiswald159/shared-settings'(behavior depends on the bundler). - Manual import (recommended and explicit): in your application's entry point (Nuxt/Vite/webpack) import the stylesheet directly:
Nuxt 3 (nuxt.config.ts):
export default defineNuxtConfig({
css: ['@jaiswald159/shared-settings/dist/global.css']
})Vite / Vue main (main.ts):
import '@jaiswald159/shared-settings/dist/global.css'
import { createApp } from 'vue'
import App from './App.vue'
// ...Simple theme toggle helper (use with the settings store)
// utils/applyThemeFromStore.ts
export function applyThemeFromSettings(store: any) {
const settings = store.GET_SETTINGS?.() || {}
// prefer an explicit theme string, fall back to darkMode boolean
const theme = settings.theme ?? (settings.darkMode ? 'dark' : 'light')
if (theme === 'dark') {
document.documentElement.setAttribute('data-theme', 'dark')
} else {
document.documentElement.removeAttribute('data-theme')
}
}Usage example in a component (after store init):
import { useSettingsStore } from '@jaiswald159/shared-settings'
import { applyThemeFromSettings } from './utils/applyThemeFromStore'
const store = useSettingsStore(appStore)
applyThemeFromSettings(store)
// Optionally watch for changes and reapply
// watch(() => store.GET_SETTINGS(), () => applyThemeFromSettings(store))Notes & compatibility
- The
stylefield is widely respected by many bundlers but not an official Node resolution field ā for the most predictable result import the stylesheet explicitly as shown above. - The
postbuildscript uses PowerShell (Windows). If you publish from Unix-based CI, replace it with a portable copy command (or use an npm postbuild JS script) to ensure cross-platform builds.
š§± Features
- ā Based on Pinia state management
- ā
Manages site-specific settings with
siteID - ā
Automatic persistence with
pinia-plugin-persistedstate - ā Reusable across multiple Vue/Nuxt projects
- ā Lightweight and modular
- ā Published as a global NPM package
- ā TypeScript support
- ā Vue 3 / Nuxt 3 compatibility
š API Reference
Settings Interface
interface Setting {
siteID: string;
[key: string]: any;
}App Store Interface
interface AppStore {
getApplicationData: (key: string) => any;
}Store Methods
UPDATE_SETTINGS(data: Setting): Save settings with persistenceGET_SETTINGS(): Retrieve settings for current sitesettings: Reactive settings array
Example CSS (Theme integration)
/* Theme variables used by the host app to sync with settings */
:root {
--color-bg: #ffffff;
--color-text: #111827;
--color-accent: #0ea5e9;
}
[data-theme="dark"] {
--color-bg: #0b1220;
--color-text: #e6edf3;
--color-accent: #60a5fa;
}
/* Basic layout utilities that read the theme variables */
.app-root {
background: var(--color-bg);
color: var(--color-text);
transition: background .2s ease, color .2s ease;
}
.btn {
background: var(--color-accent);
color: #fff;
border-radius: 6px;
padding: .5rem 1rem;
}You can apply the active theme from the store by toggling a data-theme attribute on the document element based on the store value (for example, darkMode or theme).
š Troubleshooting
Common Issues
Pinia not initialized
- Ensure
@pinia/nuxtis in modules array - Check that Pinia is properly configured
- Ensure
Persistence not working
- Verify
pinia-plugin-persistedstateis installed - Check browser console for errors
- Ensure client-side execution
- Verify
TypeScript errors
- Check TypeScript configuration
- Verify all dependencies are installed
- Restart development server
Debug Steps
- Check console logs for error messages
- Verify localStorage in browser dev tools
- Check network tab for failed requests
- Restart development server if needed
š¤ Build & Publish Guide
Build the package
npm run buildUpdate the version
npm version patch # Or use minor / major as neededPublish to NPM
npm publish --access public
ā Ensure
publishConfig.accessis set topublicinpackage.jsonfor scoped packages.
š Usage in Multiple Vue/Nuxt Apps
Once published, this package can be installed in any Vue 3 or Nuxt 3 project and used as a shared module for managing settings across multiple applications or deployments.
š ļø Notes
- Ensure you have logged in to NPM (
npm login) before publishing. - If you update files, always bump the version before publishing again.
- Remove any
pathsreferences intsconfig.jsonthat point outside the package root. - The library automatically handles client-side persistence using
localStorage.
š License
MIT Ā© jaiswald159