Why "just add one small widget" is usually hard
"I just want a little box on the Lead page showing our lead score and a quick summary. I don't need a whole new feature, and I definitely don't want to file a request with engineering just for that."
A "widget" is a small, self-contained panel of information, like a little box on a page. These small, page-specific widgets are too minor for a full engineering project, but too custom for the standard record layout โ the normal arrangement of fields on a page.
Admins need a safe way to add their own mini-panels, without any risk of breaking the rest of the CRM, or seeing data they shouldn't.
One HTML file, a sandbox, and a field allowlist
A Custom Component is a single, self-contained HTML file โ with its styling (CSS) and behavior (JavaScript) built right in, no separate files needed. You pick which CRM object it attaches to. Then you explicitly check which fields it's allowed to read, and which of those it's also allowed to write back to.
SmartLiteComponent.getRecord() to read the record and updateRecord(fields) to write to it โ never a direct database or API call.1 Build a simple Lead sidebar widget
2 Give a widget real logic: call a Custom Class
A widget isn't limited to just showing its own record's fields. It can also call a Custom Class โ the same governed-script primitive covered in the Custom Classes & Pages trail โ to fetch or compute anything else it needs, using SmartLiteComponent.callClass(apiName, params) from inside the component's own JavaScript, exactly the way a Custom Page does.
SmartLiteComponent.getRecord() always includes the current record's own ID (as id) alongside whatever fields you checked Readable โ so the widget can pass that ID straight into a Custom Class that looks up Contacts for it.listAccountContacts reads the Account ID from PARAMS, queries Contacts with CRM.query('contact', 'account_id', PARAMS.accountId), and returns the list.SmartLiteComponent.getRecord().then(r => SmartLiteComponent.callClass('listAccountContacts', { accountId: r.id })), then render the returned list as a table.Day-to-day: reps viewing the record
Day-to-day: admins
Test what you learned
SmartLiteComponent.getRecord() always includes the record's own id, which the widget passes into a Custom Class via callClass