When I first started learning Apex triggers, I wasn’t always sure when to use Trigger.new versus Trigger.old.
By focusing on how they actually work, I was able to grasp it clearly and apply it effectively.
Core Idea
Trigger.new → the data Salesforce is trying to save right now.
Trigger.old → the data that existed before the change.
Keeping this in mind, most usage scenarios become logical.
1. When to Use Trigger.new
What it represents:
Trigger.new contains the current or incoming values that Salesforce is attempting to insert or update.
Common use cases:
Validations
Setting or modifying field values
Reading updated values
Creating related records based on new data
Available in these contexts:
| Scenario | Available? |
|---|---|
| Insert | Yes |
| Update | Yes |
| Undelete | Yes |
Undelete: This trigger context runs when a record is restored from the Recycle Bin, and Trigger.new contains the record values being recovered.
Example: Validation
for (Contact con : Trigger.new) {
if (con.Email == null) {
con.Email.addError('Email is required');
}
}
This works because Trigger.new represents the data being saved right now.
2. When to Use Trigger.old
What it represents:
Trigger.old is a snapshot of the record before the change.
Common use cases:
Detecting field changes
Conditional logic based on previous values
Preventing duplicate actions
Available in these contexts:
| Scenario | Available? |
|---|---|
| Update | Yes |
| Delete | Yes |
Example: Detecting a change
for (Opportunity newOpp : Trigger.new) {
Opportunity oldOpp = Trigger.oldMap.get(newOpp.Id);
if (newOpp.StageName != oldOpp.StageName) {
// Stage has changed
}
}
Trigger.old is useful because it gives you the previous state of the record for comparison.
Tip: Trigger.oldMap and Trigger.newMap provide fast access to records by Id (Id → Record). Using maps is preferred over looping when comparing old and new values in bulk operations.
3. Using Trigger.new and Trigger.old Together
Whenever your logic depends on detecting a change, not just the current value, you need both.
Example: Detecting a meaningful update
for (Opportunity newOpp : Trigger.new) {
Opportunity oldOpp = Trigger.oldMap.get(newOpp.Id);
if (newOpp.StageName == 'Closed Won' &&
oldOpp.StageName != 'Closed Won') {
// Stage just changed to Closed Won
}
}
Using both helps prevent:
Duplicate execution
Logic running on unrelated updates
Incorrect automation
4. Context-Based Decision Table
| Requirement | Use |
|---|---|
| Validate input | Trigger.new |
| Modify fields | Trigger.new |
| Compare old vs new | Trigger.new + Trigger.old |
| Cleanup before delete | Trigger.old |
| Block record save | Trigger.new.addError() |
5. Before vs After Triggers
| Trigger Type | What to Do |
|---|---|
| Before | Modify Trigger.new |
| After | Read Trigger.new, compare with Trigger.old |
Important: You cannot modify records using Trigger.new in after triggers because the record has already been committed to the database. At this stage, Salesforce marks Trigger.new as read-only to preserve data integrity. Any further changes require a separate DML operation.
Example : Updating in after trigger using DML
List<Account> accsToUpdate = new List<Account>();
for (Account acc : Trigger.new) {
accsToUpdate.add(new Account(
Id = acc.Id,
Name = 'Updated Name'
));
}
update accsToUpdate;
6. Trigger Timeline
Think about triggers in terms of whether a record already exists and what Salesforce is doing with it.
| Trigger Context | Available Data | Purpose |
|---|---|---|
| Before Insert | Trigger.new | Record does not exist yet, modify fields before save |
| Before Update | Trigger.new + Trigger.old | Compare old vs new, modify fields before save |
| After Update | Trigger.new + Trigger.old | Record saved, read-only, detect changes |
| Before Delete | Trigger.old | Record about to be deleted, cleanup logic |
| After Delete | Trigger.old | Record deleted, read-only, audit or related logic |
Visualizing the timeline of data makes using the correct context natural.
Final Thought
Instead of memorizing rules, I always ask:
“Am I working with new data, old data, or a change between them?”
Once you answer that, the correct context (Trigger.new, Trigger.old, or both) becomes obvious.
Pro Tips
Always bulkify your triggers — loop over collections like
Trigger.newinstead of single records to avoid governor limits.Use Trigger.newMap and Trigger.oldMap for efficient lookups by Id instead of nested loops.
About Me
I’m a curious Salesforce Developer who enjoys breaking down complex concepts into simple, practical explanations.
Top comments (0)