When you need to build a custom page in NetSuite that looks and behaves like a native record form, the N/ui/serverWidget module is the tool for the job. Used inside Suitelets, it lets you construct forms, add fields and field groups, build sublists, organize content into tabs, and place buttons, all rendered server-side with NetSuite’s standard look and feel. This guide walks through the core concepts with practical examples so you can start building your own interfaces with confidence.
What Is N/ui/serverWidget?
N/ui/serverWidget is a server-side UI module. It runs in Suitelets, which are custom NetSuite pages addressable by a URL. Rather than writing raw HTML, you describe the page as a set of objects: a form, the fields on it, optional sublists for tabular data, and buttons for actions. NetSuite then renders these objects into a fully styled page. Because everything is defined in code, your forms stay consistent with the rest of NetSuite and automatically inherit its responsive styling, field behaviors, and accessibility.
Creating a Basic Form
Every Suitelet page starts with a form created by serverWidget.createForm(). You give it a title, add the fields you need, and then write it to the response so the browser renders it. A Suitelet has an onRequest entry point that receives a context object containing the request and response. On a GET request you typically build and display the form; on a POST request you read the submitted values and act on them.
Here is a minimal Suitelet that creates a form with a single text field:
/**
* @NApiVersion 2.1
* @NScriptType Suitelet
*/
define(['N/ui/serverWidget'], function(serverWidget) {
function onRequest(context) {
var form = serverWidget.createForm({ title: 'My First Suitelet' });
form.addField({
id: 'custpage_name',
type: serverWidget.FieldType.TEXT,
label: 'Full Name'
});
context.response.writePage(form);
}
return { onRequest: onRequest };
});
Adding Fields and Field Types
Fields are added with form.addField(). Each field needs a unique id (custom page fields should be prefixed with custpage_), a type, and a label. The serverWidget.FieldType enum offers many options, including TEXT, TEXTAREA, EMAIL, PHONE, DATE, CURRENCY, CHECKBOX, SELECT, and INLINEHTML. After creating a field you can refine it: mark it mandatory, set a default value, make it display-only, or change its layout width. Select fields can be populated with options using addSelectOption(), or sourced from a record type so they behave like native dropdowns.
The example below builds a small employee request form using several field types, a mandatory field, and a dropdown with manual options:
var form = serverWidget.createForm({ title: 'Time Off Request' });
var nameField = form.addField({
id: 'custpage_employee',
type: serverWidget.FieldType.TEXT,
label: 'Employee Name'
});
nameField.isMandatory = true;
form.addField({
id: 'custpage_startdate',
type: serverWidget.FieldType.DATE,
label: 'Start Date'
});
var typeField = form.addField({
id: 'custpage_type',
type: serverWidget.FieldType.SELECT,
label: 'Request Type'
});
typeField.addSelectOption({ value: 'vacation', text: 'Vacation' });
typeField.addSelectOption({ value: 'sick', text: 'Sick Leave' });
typeField.addSelectOption({ value: 'personal', text: 'Personal Day' });
Organizing with Field Groups and Tabs
As forms grow, structure becomes important. Field groups, added with form.addFieldGroup(), visually cluster related fields under a collapsible header. Tabs, added with form.addTab(), let you spread a large form across multiple pages of content so users are not overwhelmed by a single long column. When you create a field, you can assign it to a specific tab or field group using the container property, keeping your layout clean and scannable.
Building Sublists
Sublists display rows of data in a table, much like the line items on a sales order. You create one with form.addSublist(), choosing a type such as INLINEEDITOR for editable rows or LIST for read-only display. You then define columns by adding fields to the sublist, and populate rows by setting each cell’s value with setSublistValue(). Sublists are ideal for showing search results, related records, or any list the user needs to review or edit in bulk.
This example creates a read-only sublist and fills it with two rows:
var sublist = form.addSublist({
id: 'custpage_results',
type: serverWidget.SublistType.LIST,
label: 'Open Invoices'
});
sublist.addField({
id: 'custpage_invnum',
type: serverWidget.FieldType.TEXT,
label: 'Invoice #'
});
sublist.addField({
id: 'custpage_amount',
type: serverWidget.FieldType.CURRENCY,
label: 'Amount'
});
var rows = [
{ num: 'INV-1001', amt: '250.00' },
{ num: 'INV-1002', amt: '480.50' }
];
rows.forEach(function(row, i) {
sublist.setSublistValue({ id: 'custpage_invnum', line: i, value: row.num });
sublist.setSublistValue({ id: 'custpage_amount', line: i, value: row.amt });
});
Buttons and Handling Submission
To let users act on a form, add a submit button with form.addSubmitButton() or custom buttons with form.addButton(). A submit button posts the form back to the same Suitelet, where your onRequest function detects the POST method and reads the values from context.request.parameters. From there you can create or update records, run searches, or redirect the user. Custom buttons can trigger client-side functions for interactive behavior without a full page reload.
Best Practices
Always prefix custom field and sublist IDs with custpage_ to avoid collisions with native fields. Build the form once and reuse the same structure for both the GET (display) and POST (processing) branches so the layout stays consistent. Keep heavy logic such as searches and record loads efficient, since Suitelets share the same governance limits as other scripts. Validate submitted values on the server before saving, never trust client input alone, and give users clear feedback after submission using the N/ui/message module or a confirmation page.
Once you are comfortable with forms, fields, sublists, tabs, and buttons, the N/ui/serverWidget module becomes a flexible foundation for dashboards, data-entry tools, approval screens, and reporting pages, all built entirely in SuiteScript and fully integrated into the NetSuite experience.
Discover more from The NetSuite Pro
Subscribe to get the latest posts sent to your email.
Leave a Reply