---
title: 'NuxtJS'
description: "Learn how to use JSONEval-Rs in Nuxt 3, handling both SSR (Nitro) and client-side web environments comprehensively."
navigation:
  icon: 'i-simple-icons-nuxtdotjs'
seo:
  title: 'NuxtJS'
  description: "Learn how to use JSONEval-Rs in Nuxt 3, handling both SSR (Nitro) and client-side web environments comprehensively."
---

# Using JSONEval-Rs in NuxtJS (Nuxt 3)

Nuxt executes code on both the server (Nitro) and client. Isolate WebAssembly module loading based on the runtime environment.

## 1. Installation

Install both the Node bindings (for Nitro) and your Client-side bindings (Bundler for Vite/Webpack, Vanilla for Turbopack):

**For Vite/Webpack (Nuxt Default):**
```bash
yarn add @json-eval-rs/webcore @json-eval-rs/node @json-eval-rs/bundler
```

*(Note: If you have a custom setup utilizing Turbopack, you must use `@json-eval-rs/vanilla` instead of `@json-eval-rs/bundler`)*

## Built-in BigInt Support

JavaScript natively loses precision for integers extending beyond `Number.MAX_SAFE_INTEGER` (2^53 - 1). JSONEval-Rs utilizes `json-with-bigint` internally across both the Web packages and React Native bindings. This ensures that any deep 64-bit integers (`i64`) returned by the rules engine map seamlessly to JavaScript's native `BigInt` type without precision loss.

## 2. Server-Side Usage (Nitro API)

For backend evaluations (API routes or server-side only logic), you use the Node.js target.

### Standard Endpoint Validation

Create an endpoint inside your `server/api` directory:

```typescript
// server/api/validate.post.ts
import { JSONEval } from '@json-eval-rs/webcore';
import '@json-eval-rs/node'; // Registers for Node.js usage automatically

export default defineEventHandler(async (event) => {
  const data = await readBody(event);
  const schema = {
    type: "object",
    properties: {
      username: { rules: { required: { value: true, message: "Username required" } } }
    }
  };

  const evalInstance = new JSONEval({ schema: JSON.stringify(schema) });

  try {
    const result = await evalInstance.validate({ data: JSON.stringify(data) });
    return result;
  } finally {
    evalInstance.free(); // Mandatory memory cleanup
  }
});
```

### Fetching Evaluated Schema by Path

Your Nitro endpoint may only need to evaluate and query a specific schema field rather than returning the entire computed object. 

```typescript
// server/api/prices.post.ts
import { JSONEval } from '@json-eval-rs/webcore';
import '@json-eval-rs/node'; 

export default defineEventHandler(async (event) => {
  const data = await readBody(event);
  const evalInstance = new JSONEval({ schema: JSON.stringify(pricingSchema) });

  try {
    // Run evaluation engine first
    await evalInstance.evaluate({ data: JSON.stringify(data) });

    // Extract exactly the field you care about
    const computedDiscountPrice = await evalInstance.getEvaluatedSchemaByPath({
      path: "#/properties/discount_price",
      skipLayout: true
    });

    return { 
      discount: computedDiscountPrice.value, 
      applied: !computedDiscountPrice.hidden 
    };
  } finally {
    evalInstance.free(); 
  }
});
```

## 3. Client-Side Usage (Plugins/Composables)

To use JSONEval-Rs in Vue components on the client, wrap its initialization in a Nuxt Client Plugin or a Vue Composable dynamically to prevent SSR errors.

### A Component-Based Composable

Create a composable that initializes `JSONEval` lazily. By relying on dynamic imports, Nuxt won't attempt to bundle the browser WASM for Nitro.

```typescript
// composables/useEvaluator.ts
import { shallowRef, onBeforeUnmount } from 'vue';
import { JSONEval } from '@json-eval-rs/webcore';

export function useEvaluator(schema: any) {
  const evaluator = shallowRef<JSONEval | null>(null);
  const isReady = shallowRef(false);

  const init = async () => {
    // Only execute on client side
    if (!import.meta.client) return;
    
    // Dynamically load browser WASM bundler
    // Note: If using Turbopack instead of Vite, import '@json-eval-rs/vanilla'
    const wasmModule = await import('@json-eval-rs/bundler');
    
    const instance = new JSONEval({
      schema: JSON.stringify(schema),
      wasmModule
    });
    
    evaluator.value = instance;
    isReady.value = true;
  };

  onBeforeUnmount(() => {
    // Free Rust memory allocation when component unmounts
    if (evaluator.value) evaluator.value.free();
  });

  return { evaluator, isReady, init };
}
```

### Usage Inside a Vue Component

Now use the composable in your components, making sure to call `init()` inside an `onMounted` hook:

```vue
<!-- components/Validator.vue -->
<template>
  <div>
    <div v-if="!isReady">Loading WASM Evaluator...</div>
    <div v-else>
      <input v-model="username" @input="runValidation" placeholder="Enter username" />
      <ul v-if="validation?.has_error" style="color: red;">
        <li v-for="err in validation.errors" :key="err.path">{{ err.message }}</li>
      </ul>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';

const schema = {
  type: "object",
  properties: {
    username: { rules: { required: { value: true, message: "Username is required." } } }
  }
};

const username = ref('');
const validation = ref<any>(null);

const { evaluator, isReady, init } = useEvaluator(schema);

onMounted(async () => {
  await init();
});

const runValidation = async () => {
  if (!evaluator.value) return;
  
  const result = await evaluator.value.validate({
    data: JSON.stringify({ username: username.value })
  });
  
  validation.value = result;
};
</script>
```

### Subform Optimization

Targeting heavy JSON evaluations precisely to subform path contexts reduces iteration bloat during updates to large tables or repeating sections.

```typescript
const evaluateSingleRow = async (rowIndex: number, rowData: any) => {
  if (!evaluator.value) return;

  const arrayObjPath = "#/properties/shopping_cart";

  if (await evaluator.value.hasSubform(arrayObjPath)) {
    // Evaluate just this specific sub-object
    await evaluator.value.evaluateSubform({
      subformPath: arrayObjPath,
      data: JSON.stringify(rowData)
    });

    // Check specific evaluated path states
    const itemTotal = await evaluator.value.getEvaluatedSchemaByPathSubform({
      subformPath: arrayObjPath,
      path: "#/properties/row_total"
    });

    console.log(`Updated Row Total for row ${rowIndex}: ${itemTotal.value}`);
  }
};
```

### Using Web Workers for Non-Blocking UI

Move WASM evaluator instances off the main thread to prevent UI freezing.

**1. Create the Worker File (`workers/eval.worker.ts`)**
```typescript
import { JSONEval } from '@json-eval-rs/webcore';

let evaluator: JSONEval | null = null;

self.onmessage = async (e: MessageEvent) => {
  const { type, payload } = e.data;

  if (type === 'INIT') {
    const wasmModule = await import('@json-eval-rs/bundler');
    evaluator = new JSONEval({ schema: payload.schema, wasmModule });
    self.postMessage({ type: 'READY' });
  }

  if (type === 'VALIDATE' && evaluator) {
    const result = await evaluator.validate({ data: payload.data });
    self.postMessage({ type: 'RESULT', payload: result });
  }
};
```

**2. Use the Worker in your Component**
```vue
<template>
  <div>
    <button :disabled="!isReady" @click="runValidation">
      Validate in Worker
    </button>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';

const isReady = ref(false);
let worker: Worker | null = null;

onMounted(() => {
  // Vite Web Worker syntax utilized natively in Nuxt
  worker = new Worker(new URL('~/workers/eval.worker.ts', import.meta.url), { type: 'module' });

  worker.onmessage = (e) => {
    if (e.data.type === 'READY') isReady.value = true;
    if (e.data.type === 'RESULT') console.log("Validation Result:", e.data.payload);
  };

  worker.postMessage({
    type: 'INIT',
    payload: { schema: JSON.stringify({ /* schema */ }) }
  });
});

onUnmounted(() => {
  worker?.terminate();
});

const runValidation = () => {
  worker?.postMessage({
    type: 'VALIDATE',
    payload: { data: JSON.stringify({ field: "value" }) }
  });
};
</script>
```

## Important Vite Configuration

Nuxt 3 uses Vite by default. You need to enable WASM support in Vite. Update your `nuxt.config.ts` to include the `vite-plugin-wasm` or simply add top-level await if needed:

```typescript
// nuxt.config.ts
import wasm from "vite-plugin-wasm";
import topLevelAwait from "vite-plugin-top-level-await";

export default defineNuxtConfig({
  vite: {
    plugins: [
      wasm(),
      topLevelAwait()
    ]
  }
})
```

You'll need to install them:
```bash
yarn add -D vite-plugin-wasm vite-plugin-top-level-await
```
