Skip to content
Salesforce Dictionary - Free Salesforce GlossarySalesforce Dictionary
DictionaryVVisualforce Controller
DevelopmentAdvanced

Visualforce Controller

A Visualforce Controller in Salesforce is the Apex class that supplies data to a Visualforce page and handles the user actions triggered from that page.

§ 01

Definition

A Visualforce Controller in Salesforce is the Apex class that supplies data to a Visualforce page and handles the user actions triggered from that page. Each Visualforce page declares a controller in its apex:page tag and accesses the controller properties through {! } expressions in the markup. Salesforce supports three controller patterns: Standard Controllers (auto-generated for each Salesforce object, like StandardController for Account), Custom Controllers (Apex classes written by developers, with no built-in object behavior), and Controller Extensions (Apex classes that add custom logic on top of a Standard Controller).

The Controller is the server-side half of a Visualforce page; the markup is the client-side half. When the page renders, the platform creates or restores a controller instance, evaluates the {! } expressions through the controller getters, and returns the resulting HTML. When the user submits the page, the platform calls the controller setters with the form values and then invokes the action method the user clicked. The controller API version, sharing declaration, and class hierarchy all shape the page behavior at runtime.

§ 02

Visualforce Controllers: patterns, sharing, action methods, and modern alternatives

The three controller patterns: Standard, Custom, Extension

Visualforce ships three controller patterns. Standard Controllers are auto-generated for every Salesforce object: StandardController for Account, StandardController for Contact, and so on. They provide save(), edit(), delete(), and getRecord() methods plus the field-level access to the source object. Use them when the page mirrors the standard object behavior. Custom Controllers are Apex classes you write that have no built-in object integration; they provide whatever methods and properties the page needs. Use them when the page implements logic outside the standard object lifecycle (a complex search, a multi-record edit, a configuration wizard). Controller Extensions are Apex classes that take a Standard or Custom Controller in their constructor and add additional methods to it; the page declares both. Use extensions to enhance the standard behavior without rewriting it from scratch.

Sharing declarations and the running user context

Apex classes (including controllers) declare their sharing behavior with three keywords: with sharing (respects the running user sharing rules), without sharing (bypasses sharing), inherited sharing (inherits from the calling class). For Visualforce Controllers, the choice matters. A Controller that displays a list of records should typically use with sharing so the user only sees records they have permission to access. A Controller that runs administrative logic on behalf of the user (a bulk record creation script that needs to write to records the user does not own) may need without sharing carefully. The default for a controller declared without an explicit sharing keyword is without sharing, which surprises developers expecting the safer with sharing default. Mature codebases always specify the sharing behavior explicitly.

Action methods and the @action attribute pattern

Controllers expose action methods that the page invokes when the user clicks a button, submits a form, or triggers a specific event. Action methods must be public (or global), must take no parameters, and must return a PageReference (often null to stay on the current page). The page declares the action through apex:commandButton action={!save}, apex:commandLink, or apex:actionSupport. The platform invokes the action after the setters but before the page rendering, so the action method sees the user-provided values via the controller properties. Action methods are the place to implement business logic: validate the input, perform DML, call out to external services, and decide what page to navigate to next.

Properties, getters, and the data binding pattern

Visualforce binds page expressions to controller properties through the getter pattern. {!myProperty} calls getMyProperty() on the controller. The controller getter computes or returns the value; the platform renders it in the page. Properties can be simple fields (a String, an Integer), complex types (a custom Wrapper class, a list of sObjects), or computed values (a getter that calculates on every call). For lists used in repeated regions (apex:repeat, apex:dataTable), the getter is called once and the resulting list is iterated. For scalar values, the getter may be called multiple times depending on how often the expression appears in the page. Mature controllers cache expensive computation in instance variables to avoid recalculating on every getter call.

Testing Visualforce Controllers in Apex test methods

Apex tests can exercise Visualforce Controllers directly without going through page rendering. The test creates the controller instance (passing test data into the constructor for custom controllers, or a StandardController wrapping a test sObject), calls the controller action methods and getters, and asserts the resulting state. For pages that use a Standard Controller, the test wraps a test sObject in an ApexPages.StandardController and passes it to the Controller Extension constructor. For pages that use page parameters, the test sets ApexPages.currentPage().getParameters().put(name, value) before invoking the controller. Aim for 75 percent coverage on the controller; controllers without test coverage cannot be deployed to production.

Controllers in 2026: Visualforce place in the modern stack

Visualforce Controllers remain in production at most established Salesforce orgs but receive no new platform investment. New UI development on Salesforce in 2026 should use Lightning Web Components with @AuraEnabled Apex methods, not Visualforce Controllers. The Apex side of the LWC pattern is conceptually similar (an Apex class that the UI calls), but the API surface is different: @AuraEnabled methods are explicit RPC endpoints, not implicit getters and setters tied to a page lifecycle. Existing Visualforce Controllers in production should be left in place unless they need refactoring; rewriting working code as LWC rarely justifies the migration cost. Plan rewrites during major Lightning Experience modernization projects, not as standalone work. Treat Visualforce Controller expertise as a maintenance skill rather than a growth skill.

§ 03

Writing a maintainable Visualforce Controller

Writing a Visualforce Controller well is mostly about picking the right pattern and following the lifecycle conventions. The four-step routine covers: pick Standard Controller, Custom Controller, or Controller Extension based on the use case; declare sharing behavior explicitly; implement action methods with the right signature; and test the controller in Apex unit tests. Each step is a small decision; the cumulative effect is the difference between a maintainable controller and a brittle one that future developers struggle to inherit.

  1. Pick the controller pattern based on the use case

    For a page that mostly displays and edits standard object behavior (a custom Account detail page, a custom Opportunity edit page), pick the Standard Controller for the object. Declare the controller on the apex:page tag: standardController=Account. For a page that implements logic outside the standard object lifecycle (a multi-record edit, a custom wizard, a complex search), write a Custom Controller. Declare it on the apex:page tag: controller=MyCustomController. For a page that needs the standard behavior plus a small extension, write a Controller Extension and declare both: standardController=Account extensions=MyExtension. Document the choice in a class header comment.

  2. Declare sharing behavior explicitly

    Every controller class should explicitly declare its sharing behavior with one of three keywords: with sharing, without sharing, or inherited sharing. with sharing respects the running user sharing rules and is the safer default for most controllers. without sharing bypasses sharing and is appropriate only when the controller intentionally runs administrative logic. inherited sharing inherits from the calling class and is useful for utility classes. Default behavior (no keyword) is without sharing, which surprises developers expecting safer behavior. Always specify the keyword explicitly; reviewers should flag any controller class that omits it.

  3. Implement action methods with the right signature

    Action methods must be public or global, take no parameters, and return a PageReference (often null to stay on the current page) or void. Action methods called from Visualforce buttons or links have to match this signature; otherwise the page does not call them. Inside the action method, validate the inputs received through setters, perform any DML or callouts, and decide what page to navigate to next (return a new PageReference for navigation, return null to stay). Handle exceptions explicitly with try-catch; uncaught exceptions surface as ugly platform error messages rather than the controlled error display the page can render.

  4. Test the controller in Apex unit tests

    Write @isTest methods that exercise the controller directly. For Standard Controller-based pages, wrap a test sObject in ApexPages.StandardController and pass it to the Controller Extension constructor. For Custom Controllers, instantiate the class with test data. Call the controller action methods and assert the resulting state of the controller and any DML side effects. Set ApexPages.currentPage().getParameters() to simulate URL parameters. Aim for 75 percent coverage on the controller; without it, the controller cannot deploy to production. Add the tests to your CI pipeline so changes to the controller logic get validated automatically.

Gotchas
  • The default sharing behavior for a controller without an explicit keyword is without sharing. Always specify the keyword; the default is rarely what you want.
  • Action methods called from Visualforce must take no parameters. Methods that take parameters cannot be invoked from commandButton or commandLink; refactor to read inputs through setters instead.
  • Getters can be called many times during page rendering. Expensive computation in getters slows down the page; cache the result in an instance variable and have the getter return the cached value.
  • Standard Controllers vary in capability per object. Some objects do not support all Standard Controller methods; check the documentation for the specific object before relying on standardized behavior.
  • Visualforce is in maintenance mode. New custom UI work should use Lightning Web Components with @AuraEnabled Apex methods, not new Visualforce Controllers.
§

Trust & references

Official documentation

Straight from the source - Salesforce's reference material on Visualforce Controller.

Keep learning

Hands-on resources to go deeper on Visualforce Controller.

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 is a Visualforce Controller?

Q2. What types exist?

Q3. When use a standard controller?

§

Discussion

Loading…

Loading discussion…