Skip to content
Salesforce Dictionary - Free Salesforce GlossarySalesforce Dictionary
DictionaryTTrigger Context Variable
DevelopmentAdvanced

Trigger Context Variable

A Trigger Context Variable is one of a set of implicit static variables that Apex provides inside a trigger to describe the event being processed.

§ 01

Definition

A Trigger Context Variable is one of a set of implicit static variables that Apex provides inside a trigger to describe the event being processed. They tell the trigger what is happening (insert, update, delete, undelete), when it is happening (before or after the database commit), and which records are involved.

The full set covers seven boolean flags for the event type and four collections for the affected records. The flags are isInsert, isUpdate, isDelete, isUndelete, isBefore, isAfter, and isExecuting. The collections are Trigger.new, Trigger.newMap, Trigger.old, and Trigger.oldMap. Every trigger has access to these variables for free; you do not declare them, and you cannot reassign them.

§ 02

The implicit variables every Apex trigger gets for free

The event flags and what they tell the trigger

Five flags describe which DML event fired the trigger. Trigger.isInsert is true for insert and upsert events. Trigger.isUpdate is true for update and upsert events. Trigger.isDelete is true for delete events. Trigger.isUndelete is true when a previously soft-deleted record is restored from the Recycle Bin. Trigger.isExecuting is true whenever the current Apex code is running inside a trigger context rather than from a class method called outside a trigger. Two more flags describe timing: Trigger.isBefore is true when the trigger runs before the database write, Trigger.isAfter is true when it runs after.

Trigger.new and Trigger.newMap

Trigger.new is a List of sObject containing the new versions of the records being processed. In a before insert trigger, you can mutate fields on these records and the changes write to the database when the trigger returns. In any after context, Trigger.new is read-only and any field assignment throws a runtime error. Trigger.newMap is a Map of Id to sObject keyed by the record Id, useful when you need fast Id-based lookups instead of iterating the list. The Map is not available in before insert because the records do not have Ids yet; in every other context where Trigger.new is populated, the Map is too.

Trigger.old and Trigger.oldMap

Trigger.old is a List of sObject containing the old versions of the records as they were before the update or delete. Trigger.oldMap is the same data keyed by Id. Both are read-only in every context. They are only populated for update and delete events; in an insert or undelete trigger, accessing Trigger.old returns null and Trigger.oldMap returns an empty map. The classic use case is comparing old to new to detect a field change: oldMap.get(rec.Id).Status != rec.Status tells you the Status moved between the old and new state.

Availability matrix: which variables are populated when

The full availability matrix has eight cells. Before insert: Trigger.new is writable, Trigger.newMap is empty because there are no Ids yet, Trigger.old and Trigger.oldMap are null. After insert: Trigger.new and Trigger.newMap are read-only, old is null. Before update: Trigger.new is writable, while Trigger.newMap, Trigger.old, and Trigger.oldMap are all populated and read-only. After update: all four are populated and read-only. Before and after delete: Trigger.old and Trigger.oldMap are populated and read-only; new is null. After undelete: Trigger.new and Trigger.newMap are populated and read-only; old is null. There is no before-undelete trigger.

Why the variables are List and Map, not single records

Triggers fire in bulk. Salesforce gathers all the records affected by a single DML operation and passes them to the trigger as a batch of up to 200. This is why the variables are List and Map, not single sObject references. A bulk update of 5,000 records produces 25 separate trigger invocations, each with up to 200 records in Trigger.new. Writing trigger logic that loops over Trigger.new and treats each record independently is the bulkification rule: do not run a SOQL query inside the loop, do not run DML inside the loop, do everything in collections built from the trigger variables.

Common patterns built on top of the context variables

The recursive-trigger prevention pattern uses a static set to remember which Ids have already been processed in the current transaction; the trigger guards its work with a check like if (!ProcessedIds.contains(rec.Id)). The field-changed pattern uses oldMap.get(rec.Id).Field != rec.Field to detect transitions. The before-after split puts validation in before triggers (so the operation can throw addError and block the save) and side effects in after triggers (so the records have Ids and the database state is committed). Trigger.size gives the record count when you need it; Trigger.isExecuting lets handler classes share code between trigger and non-trigger callers.

Order of execution and where the context variables fit

When a record is saved, Salesforce runs a precise order of execution: system validation, before triggers, custom validation, database save without commit, after triggers, assignment rules, auto-response rules, workflow, processes, flows, post-commit logic, then commit. Trigger context variables are available throughout the before and after trigger phases. Trigger.new in a before trigger lets you mutate the inbound record before validation runs; Trigger.new in an after trigger reflects the post-save state, including any default values the platform filled in. Understanding where context variables sit in the order of execution is the difference between a working trigger and one that mysteriously misses changes made by other automations later in the order.

§ 03

Use trigger context variables in a bulkified Apex trigger

Trigger context variables are best understood by writing a trigger that uses them. The steps below walk through the canonical patterns: detect a field change, prevent recursion, and split before vs after work.

  1. Branch on event type with the boolean flags

    Inside the trigger, branch on Trigger.isBefore + Trigger.isInsert vs Trigger.isAfter + Trigger.isUpdate to handle each context. Combine the timing flag and the event flag explicitly; do not assume a single trigger only ever runs one event.

  2. Iterate Trigger.new and read Trigger.oldMap by Id

    For an update trigger, iterate Trigger.new with a for-each loop and look up the matching old version with Trigger.oldMap.get(rec.Id). Compare the fields you care about. Build a List of records that need follow-up work outside the loop.

  3. Move SOQL and DML outside the loop

    Collect Ids or sObjects inside the loop. Run SOQL once over the collected Ids. Run DML once over the collected list. Bulkified trigger code processes 200 records in the same query and DML budget as 1 record.

  4. Guard against recursion with a static set

    Add a static Set of Id to a helper class that the trigger calls. Before processing a record, check whether its Id is in the set; if so, skip. After processing, add the Id. Recursion guards are essential for triggers that update their own object.

  5. Put validation in before, side effects in after

    Validations that should block the save call addError on Trigger.new in a before trigger. Side effects that need the record to exist (insert child records, post Chatter feed items) belong in an after trigger because the parent has an Id and is committed.

Trigger.sizeremember

Integer count of records in the batch. Useful for logging or guards that skip work when the batch is over a threshold.

Trigger.isExecutingremember

True when code is running inside a trigger. Lets a handler class share its work between trigger and non-trigger callers.

Trigger.operationTyperemember

Enum that returns the specific operation (BEFORE_INSERT, AFTER_UPDATE, etc.). Cleaner than checking pairs of boolean flags for some patterns.

Gotchas
  • Trigger.newMap is empty in before insert because the records do not have Ids yet. Code that iterates the map will silently process zero rows in before insert and admins miss the bug until production.
  • Trigger.new is read-only in after triggers. Trying to assign a field value to a record in Trigger.new from an after context throws a runtime error and rolls back the transaction.
  • Triggers fire in batches of up to 200. A bulk update of 5,000 records produces 25 separate trigger invocations, each with its own Trigger.new of up to 200. Static state used for recursion guards persists across all 25 invocations because they share a transaction.
§

Trust & references

Sources

Cross-checked against the following references.

Official documentation

Straight from the source - Salesforce's reference material on Trigger Context Variable.

Keep learning

Hands-on resources to go deeper on Trigger Context Variable.

Was this entry helpful?
Help us write better definitions. Quick reactions or detailed edit suggestions.

About the Author

Dipojjal Chakrabarti is a B2C Solution Architect with 29 Salesforce certifications and over 13 years in the Salesforce ecosystem. He runs salesforcedictionary.com to help admins, developers, architects, and cert/interview candidates sharpen their fundamentals. More about Dipojjal.

§

Test your knowledge

Q1. What are Trigger Context Variables?

Q2. What does Trigger.new contain?

Q3. What does Trigger.isInsert tell you?

§

Discussion

Loading…

Loading discussion…