# Choosing between User Event and Client scripts in NetSuite

> Source: https://netray.co/erp-help/netsuite-user-event-vs-client-script-patterns
> Publisher: Netray (netray.co), AI and integration engineering for ERP and manufacturing systems.
> Section: ERP Help

## Short answer

User Event scripts (beforeLoad, beforeSubmit, afterSubmit) run server-side on every save regardless of how the record was created, including CSV import, web services and workflows, and are the only reliable place to enforce business rules. Client scripts (pageInit, fieldChanged, saveRecord, postSourcing) run only in an interactive browser session and can be bypassed entirely by any non-UI record save, so they should handle UX (field visibility, warnings, defaults) rather than validation that must never be skipped.

## Kind

advanced

## System

netsuite

## Erp

Oracle NetSuite

## Module

SuiteScript 2.x / User Event and Client Scripts

## Query

when to use user event script vs client script in netsuite

## Alternate Queries

- netsuite beforeSubmit vs client script saveRecord
- netsuite user event script server side validation
- netsuite client script fieldChanged vs user event
- netsuite user event afterSubmit vs client script

## Applies To

SuiteScript 2.x User Event and Client Script types, all NetSuite editions and all record creation paths (UI, CSV import, SuiteScript, SuiteTalk, workflows)

## Steps

### Decide which script type owns which behavior

**Items:**

If a rule must hold true no matter how the record is created (UI, CSV import, integration, workflow action), put it in a User Event script's beforeSubmit, since that is the one entry point every save path goes through.

If a rule is purely about interactive usability, such as showing/hiding a field, popping a confirmation dialog, or auto-populating a field as the user types, put it in a Client script's fieldChanged or pageInit; these never run for a CSV import or API call.

Never rely on a Client script's saveRecord function alone to block an invalid save; treat it as a first line of UX feedback and always duplicate the actual validation in a User Event beforeSubmit so non-UI paths cannot slip through.

Use beforeLoad for read-only UI adjustments that need to happen before the page renders, such as removing a button or setting a field to display-only based on role or record state.

Use beforeSubmit for validation and last-chance field manipulation before the record is committed to the database; context.newRecord and context.oldRecord are both available here for comparing changes.

Use afterSubmit for anything that needs the record's final, committed internal ID, or that triggers side effects on other records (creating a related record, calling an external API, updating a linked transaction).

In Client scripts, use postSourcing rather than fieldChanged when you need to react after NetSuite's automatic field sourcing has finished populating dependent fields, to avoid reading stale values.

Check context.type in User Event scripts (create, edit, delete, xedit, copy) and skip logic that should not apply to inline edits or mass updates if the rule only makes sense on full record saves.

## Sections

### Why Client scripts alone are not enough for validation

**Body:**

A Client script only executes inside a browser session where NetSuite's UI is loaded and the script deployment applies to that form. A record created through CSV import, a Suitelet posting directly to record.save(), a RESTlet, SuiteTalk, or a scheduled script never triggers any Client script logic at all, so any business rule enforced only there is trivially bypassed by every non-interactive path.

This is the single most common design mistake in new NetSuite customizations: a saveRecord function that looks like it blocks bad data, until an integration or a bulk CSV import creates records that violate the same rule with no error, because the Client script simply never ran.

The safe pattern is to treat Client scripts as user experience only, and to duplicate any rule that must always hold in a User Event beforeSubmit, which does run for every save path without exception.

### beforeLoad, beforeSubmit and afterSubmit in practice

**Body:**

beforeLoad runs when the record is being rendered for view/edit/create in the UI (and for print/pdf/email contexts too, distinguished by context.type), and is the right place for UI-only adjustments like context.form.getField(...).updateDisplayType(...); it has no effect on records saved without going through that render path.

beforeSubmit runs on every save right before the database write, with both context.newRecord (the record as it will be saved) and, for edits, context.oldRecord (the record's prior state) available, making it the natural place to compare old versus new values and block or correct a change.

afterSubmit runs after the record is committed and has a real internal ID, which matters for anything that needs to reference the just-saved record from elsewhere, such as creating a linked custom record or firing a webhook; trying to do this kind of work in beforeSubmit before the ID exists is a common source of bugs.

**Code:**

define(['N/record'], function(record) {
  function beforeSubmit(context) {
    var newRec = context.newRecord;
    var amount = newRec.getValue({ fieldId: 'total' });
    if (amount > 100000 && !newRec.getValue({ fieldId: 'custbody_vp_approved' })) {
      throw new Error('Orders over 100000 require VP approval before saving.');
    }
  }
  return { beforeSubmit: beforeSubmit };
});

### context.type and avoiding unintended triggers

**Body:**

User Event scripts fire for xedit (inline grid edit) and massupdate contexts too, not just full record edits, which surprises developers whose logic assumes a fully loaded record with every field populated as it would be from the main edit form. Checking context.type and returning early for contexts the logic was not designed for avoids subtle bugs on inline edits.

Similarly, a copy context (using the UI's Make Copy) triggers beforeSubmit and afterSubmit as if it were a create, which is usually desired for default values but can be wrong for logic that assumes a first-time-ever creation, such as sending a one-time welcome notification.

## Pitfalls

- Enforcing a required business rule only in a Client script's saveRecord, letting CSV imports and integrations bypass it entirely.
- Reading a sourced field's value in fieldChanged before NetSuite finishes sourcing, getting a stale or empty value instead of using postSourcing.
- Doing heavy record.load or search calls in beforeLoad on a frequently viewed list, slowing down page render for every user who opens that record type.
- Not distinguishing context.type in a User Event script, so mass updates or inline edits trigger logic meant only for full-form saves.
- Trying to reference the record's internal ID inside beforeSubmit on a create, when it does not exist yet; that has to wait for afterSubmit.
- Deploying a Client script only to the form used in the UI while forgetting that other forms (or the mobile/CSV paths) for the same record type need the same protection at the User Event level anyway.

## Ai Angle

When reviewing a customization, ERPray can trace which record creation paths (UI form, CSV import map, integration, workflow action) actually exist for a given record type in a customer's account, and flag validation logic that lives only in a Client script where a non-UI path would bypass it. That grounded, account-specific check is more reliable than a generic code review checklist.

## Faq

### Do Client scripts run during a CSV import?

**Answer:** No. CSV import writes records directly and does not load the record edit form, so no Client script deployment fires. Any validation that also needs to apply to CSV-imported records must live in a User Event script instead.

### Can a User Event script cancel a save the way a Client script can?

**Answer:** Yes, throwing an error inside beforeSubmit prevents the record from being saved and surfaces the error message to whatever initiated the save, whether that is the UI, a CSV import row, or an API call, which is exactly why it is the reliable place for hard validation.

### Should beforeLoad ever contain validation logic?

**Answer:** No, beforeLoad only affects how the record is displayed before any save happens; it cannot block or alter what gets submitted. Validation belongs in beforeSubmit, while beforeLoad is for read-only UI adjustments like hiding fields or buttons.

### Why does my User Event script behave differently on inline edit than on the full form?

**Answer:** Inline edits (xedit context) submit only the changed field rather than the full record, so context.newRecord may not have every field populated the way a full-form save would. Check context.type and handle xedit separately if the logic depends on fields that are not part of the inline edit.

## Search terms

netsuite user event vs client script, netsuite beforeSubmit vs saveRecord, netsuite beforeLoad afterSubmit explained, netsuite client script bypass csv import, netsuite fieldChanged postSourcing, netsuite context.type xedit, suitescript validation best practice, netsuite user event script context.oldRecord

---
Talk to an engineer: https://netray.co/contact?intent=erp_ai | Machine catalog: https://netray.co/agent/catalog.json
