If you are learning Salesforce development, Apex Triggers are one of the first things you need to master.
Many beginners feel confused when they first see trigger code. It looks complex, but once you understand the basics, it becomes very simple.
In this guide, you will learn what Apex Triggers are, how they work, and how to write your first trigger with real examples.
What is an Apex Trigger?
An Apex Trigger is a piece of code that runs automatically before or after a record is inserted, updated, deleted, or undeleted in Salesforce.
Think of it like this:
Every time something happens to a record — a trigger can react to it automatically.
Simple example: When a new Account is created, automatically create a related Task for the owner.
When to Use Apex Trigger
Use a trigger when:
- Flow or Process Builder cannot handle the logic
- You need complex conditions or calculations
- You need to update related records automatically
- You need to validate data before saving
Trigger Syntax (Basic Structure)
java
trigger TriggerName on ObjectName (trigger events) {
// your code here
}
Real example:
java
trigger AccountTrigger on Account (before insert, after insert) {
// your code here
}
Types of Triggers
There are two main types:
1. Before Trigger
Runs before the record is saved to the database
Use it when:
- You want to validate or modify field values before saving
- Example: Check if phone number is empty before inserting
java
trigger AccountTrigger on Account (before insert) {
for (Account acc : Trigger.new) {
if (acc.Phone == null) {
acc.Phone = '0000000000';
}
}
}
2. After Trigger
Runs after the record is saved to the database
Use it when:
- You want to create or update related records
- Example: Create a Task after a new Lead is inserted
java
trigger LeadTrigger on Lead (after insert) {
List<Task> tasks = new List<Task>();
for (Lead ld : Trigger.new) {
Task t = new Task();
t.Subject = 'Follow up with Lead';
t.WhoId = ld.Id;
tasks.add(t);
}
insert tasks;
}
Trigger Events
You can combine multiple events in one trigger:
| Event | When it runs |
|---|---|
| before insert | Before new record is saved |
| after insert | After new record is saved |
| before update | Before existing record is updated |
| after update | After existing record is updated |
| before delete | Before record is deleted |
| after delete | After record is deleted |
| after undelete | After record is restored |
Trigger Context Variables
These are special variables available inside every trigger:
| Variable | What it contains |
|---|---|
| Trigger.new | List of new records |
| Trigger.old | List of old records (before update) |
| Trigger.newMap | Map of new records with Id |
| Trigger.oldMap | Map of old records with Id |
| Trigger.isInsert | True if insert operation |
| Trigger.isUpdate | True if update operation |
| Trigger.isDelete | True if delete operation |
Real World Example — Complete Trigger
Scenario: When an Opportunity is marked as Closed Won, automatically create a follow-up Task
java
trigger OpportunityTrigger on Opportunity (after update) {
List<Task> tasks = new List<Task>();
for (Opportunity opp : Trigger.new) {
Opportunity oldOpp = Trigger.oldMap.get(opp.Id);
if (opp.StageName == 'Closed Won' &&
oldOpp.StageName != 'Closed Won') {
Task t = new Task();
t.Subject = 'Send thank you email to customer';
t.WhatId = opp.Id;
t.ActivityDate = Date.today().addDays(1);
tasks.add(t);
}
}
if (!tasks.isEmpty()) {
insert tasks;
}
}
Apex Trigger Best Practices
1. One Trigger Per Object Never create multiple triggers on the same object — it causes unpredictable behavior
2. No Logic in Trigger Keep trigger code clean — move all logic to a Handler class
java
trigger AccountTrigger on Account (before insert, after insert) {
AccountTriggerHandler.handleBeforeInsert(Trigger.new);
}
3. Bulkify Your Code Always write code that can handle multiple records at once — never put SOQL inside a loop
❌ Wrong:
java
for (Account acc : Trigger.new) {
List<Contact> cons = [SELECT Id FROM Contact WHERE AccountId = :acc.Id];
}
✅ Right:
java
Set<Id> accountIds = new Set<Id>();
for (Account acc : Trigger.new) {
accountIds.add(acc.Id);
}
List<Contact> cons = [SELECT Id FROM Contact WHERE AccountId IN :accountIds];
4. Always Check for Empty Lists Before DML operations, always check if list is empty
java
if (!tasks.isEmpty()) {
insert tasks;
}
5. Use Try-Catch for Error Handling Always handle exceptions gracefully
Common Mistakes Beginners Make
1. SOQL inside for loop This hits governor limits very quickly — always move SOQL outside loops
2. Hardcoding values Never hardcode Ids or values — use Custom Labels or Custom Settings
3. Not bulkifying code Always assume multiple records will be processed at once
4. Multiple triggers on same object Always use one trigger per object with a handler class
Apex Trigger vs Flow — When to Use What
| Scenario | Use |
|---|---|
| Simple record update | Flow ✅ |
| Complex calculations | Apex Trigger ✅ |
| No code preferred | Flow ✅ |
| Related record creation with conditions | Apex Trigger ✅ |
| Beginner friendly | Flow ✅ |
Conclusion
Apex Triggers are a powerful tool in Salesforce development. Once you understand before vs after triggers and context variables, you can automate almost anything.
Start with simple before insert triggers, then move to after insert and after update. Practice on a free Developer org and build real examples.
The more you practice, the more confident you will become.
Found this helpful? Share it with your Salesforce learning community. Have questions? Drop them in the comments below.
Read More: