11.1 Visualforce Pages & Markup
Key Takeaways
- A Visualforce page is an apex:page root with markup that mixes standard components, HTML, CSS, and JavaScript bound to a controller via merge-field expressions
- Use apex:form with inputField/outputField and pageBlock structure for layout; dataTable and apex:repeat iterate collections for lists
- Expression syntax {! } binds properties and methods on the controller or standard controller record; static resources host CSS, JS, and images
- View state stores form and controller state between postbacks and is capped at 170 KB per page (the old 135 KB figure is stale)—large pages and excess state cause ViewStateMaxSizeExceeded
- Platform Developer I still tests Visualforce markup and MVC patterns; for new UI, prefer Lightning Web Components while retaining VF for legacy, PDF, and some embedded scenarios
11.1 Visualforce Pages & Markup
Quick Answer: A Visualforce (VF) page is server-rendered markup under an
<apex:page>root. You compose standard components (form,pageBlock,inputField,outputField,dataTable,repeat, and others), bind data with{! }merge expressions, attach static resources for CSS/JS/images, and manage view state size on postbacks. On Platform Developer I, VF remains a tested User Interface skill even though Lightning Web Components (LWC) are preferred for most new interactive UI.
Visualforce is the classic Salesforce MVC view technology: markup is the View, the controller (standard, custom, or extension) is the Controller, and sObjects/fields are the Model. This section focuses on pages and markup. Section 11.2 covers controllers; 11.3 covers Lightning Experience embedding and migration judgment.
Why Visualforce Still Matters on PDI
UI is about 25% of the current blueprint. Exam items may ask you to:
- Read a page snippet and predict what renders or which attribute is wrong
- Choose standard controller vs custom controller vs extension (next section)
- Recognize view state problems or missing
apex:formaround command buttons - Contrast VF with Aura/LWC for a greenfield requirement
You are not expected to build a full enterprise SPA in VF on the exam. You are expected to know structure, binding, common components, and when VF is the wrong default for new work.
The apex:page Root
Every page starts with <apex:page>. Important attributes you should recognize:
| Attribute | Role |
|---|---|
standardController | Name of the standard or custom object (for example, Account)—provides the record context and standard actions |
extensions | Comma-separated Apex classes that extend the standard (or custom) controller |
controller | Apex class name for a custom controller (no standard controller) |
recordSetVar | Variable name for the list of records when using a standard list controller |
action | Method run before the page renders (for example, init logic) |
renderAs | Often pdf for PDF generation |
sidebar / showHeader / applyHtmlTag / applyBodyTag | Chrome and HTML shell control—especially relevant when embedding |
docType | HTML doctype when you need modern HTML/CSS behavior |
lightningStylesheets | Applies Lightning-like styling in Lightning Experience (see 11.3) |
Minimal page with a standard controller:
<apex:page standardController="Account">
<apex:form>
<apex:pageBlock title="Account">
<apex:pageBlockSection>
<apex:outputField value="{!Account.Name}"/>
<apex:inputField value="{!Account.Phone}"/>
</apex:pageBlockSection>
<apex:pageBlockButtons>
<apex:commandButton action="{!save}" value="Save"/>
</apex:pageBlockButtons>
</apex:pageBlock>
</apex:form>
</apex:page>
Rules of thumb
- You cannot use both
controllerandstandardControlleron the same page as peers in the “pick one primary controller” sense used on the exam: custom controller or standard controller (+ optional extensions). extensionsrequire a constructor that accepts the standard (or custom) controller type.- Without
apex:form, postback components likeapex:commandButtoncannot submit the view state correctly for typical save/edit patterns.
Expression Syntax: {! }
Merge fields evaluate expressions against the controller context:
{!Account.Name}— field on the standard controller’s record (object API name as root whenstandardController="Account"){!myProperty}— getter property on a custom controller or extension{!myMethod}— action method for buttons/links (no parentheses in markup){!IF(condition, a, b)},{!URLFOR(...)},{!$ObjectType.Account.fields.Name.label}— formula-style and global variables
Global merge variables you will see: $User, $Profile, $ObjectType, $Resource, $Label, $Page, $Action, $Setup, and more.
Static resource reference:
<apex:stylesheet value="{!$Resource.MyStyles}"/>
<apex:includeScript value="{!$Resource.MyScript}"/>
<!-- Zip static resource path -->
<apex:image value="{!URLFOR($Resource.MyZip, 'images/logo.png')}"/>
Exam trap: Expressions are case-insensitive for many identifiers but field/API names must match the schema. A misspelled property fails at runtime or design-time validation depending on context.
Core Standard Components
Structure and layout
| Component | Purpose |
|---|---|
apex:pageBlock | Salesforce-styled block with title |
apex:pageBlockSection | Multi-column section inside a pageBlock |
apex:pageBlockSectionItem | Label/value pair control when not using inputField/outputField auto-layout |
apex:pageBlockButtons | Button bar (top/bottom of pageBlock) |
apex:pageBlockTable | Table bound to a list; Salesforce styling |
apex:sectionHeader | Title/subtitle header |
apex:tabPanel / apex:tab | Tabbed UI |
Forms and fields
| Component | Purpose |
|---|---|
apex:form | Required container for postbacks and view state |
apex:inputField | Renders the correct editor for the field type and enforces FLS presentation |
apex:outputField | Read-only field display with correct type formatting |
apex:inputText / inputTextarea / inputCheckbox / selectList | Generic inputs when not binding typed sObject fields |
apex:commandButton / commandLink | Submit form; action points to controller method |
apex:actionSupport / actionFunction / actionPoller / actionStatus | Partial-page AJAX behaviors |
apex:message / apex:messages / apex:pageMessages | Validation and Apex addMessage display |
inputField vs generic inputs: Prefer inputField when bound to sObject fields so field type, picklist values, and dependent picklists work with less custom code. Generic inputs need manual validation and do not automatically apply field-level help/type widgets.
Iteration: dataTable, pageBlockTable, repeat
<apex:pageBlockTable value="{!Account.Contacts}" var="c">
<apex:column value="{!c.Name}"/>
<apex:column value="{!c.Email}"/>
</apex:pageBlockTable>
<apex:dataTable value="{!items}" var="row" id="tbl">
<apex:column headerValue="Name">
<apex:outputText value="{!row.Name}"/>
</apex:column>
</apex:dataTable>
<apex:repeat value="{!items}" var="row">
<div class="row">{!row.Name}</div>
</apex:repeat>
| Component | When to choose |
|---|---|
pageBlockTable | Standard Salesforce table look inside pageBlocks |
dataTable | HTML table with more control; still server-side iteration |
repeat | Full markup control (divs, custom HTML)—no automatic table chrome |
All three need a value collection and a var loop variable. Nested repeats are allowed but increase view state and complexity.
Incorporating HTML, CSS, and JavaScript
Visualforce allows raw HTML inside the page (subject to platform security and component nesting rules). Common patterns:
- Inline styles/scripts for tiny demos—avoid for production; hard to cache and reuse.
- Static resources for CSS, JS libraries, fonts, and images (single files or zips).
apex:includeScript/apex:stylesheetso dependencies load in a controlled order.- JavaScript remoting (
@RemoteAction) oractionFunctionwhen you need client→Apex without full classic postbacks (remoting is controller-side; markup still hosts the JS).
Security note: Escape untrusted data. Prefer apex:outputText with default escaping over dumping raw HTML. Cross-site scripting risks appear when you disable escaping (escape="false") carelessly.
CSS scope: Classic VF pages do not automatically look like Lightning. Use lightningStylesheets="true" (11.3) or custom CSS for Lightning Experience polish. Component IDs in the DOM are often prefixed—use $Component in expressions when wiring JS to VF-generated client IDs.
Static Resources
Static resources are org-stored files (max size per resource is platform-limited; zip archives are common). Benefits for the exam narrative:
- Cached and versioned with the org
- Referenced with
$Resource - Shared across many pages
- Better than Document/Attachment links for UI assets
Upload a zip with css/app.css and js/app.js, then reference paths with URLFOR($Resource.ZipName, 'css/app.css').
View State: Concept and Size Limits
View state is the serialized snapshot of the page’s non-transient controller state, form data, and component tree that Salesforce posts back with each form submission so the page can restore context between requests.
Why it matters
- Large tables, non-transient collections, and deep component trees inflate view state
- Exceeding the limit throws
ViewStateMaxSizeExceededException. The documented maximum is 170 KB per page, and the runtime error text says so literally: “Maximum view state size limit (170KB) exceeded.” Older study material still quotes the pre-increase 135 KB figure—treat 135 KB as stale and 170 KB as current - Only applies to stateful VF form postbacks—not to pure client-side LWC the same way
Mitigations the exam expects you to recognize
| Technique | Effect |
|---|---|
Mark large controller properties transient | Excluded from view state; re-query or rebuild each request |
| Reduce rows shown; paginate (StandardSetController) | Smaller component/state graph |
Prefer outputField / fewer input components when possible | Less form state |
| Avoid stuffing huge Maps/Lists into non-transient properties | Direct size cut |
| Use JavaScript remoting / redesign for partial patterns | Can avoid full classic view-state postbacks for some interactions |
| Split complex UIs into multiple pages or Lightning components | Architectural relief |
Exam scenario: A page works for admins testing with 5 rows but fails for users with 500 child records in a non-paginated pageBlockTable bound to a non-transient list—classic view-state (or heap) pain. Pagination and transient are first-line answers.
When VF Appears vs LWC Preference for New UI
| Situation | Lean toward |
|---|---|
| New interactive record UI, App Builder composition, mobile-friendly Lightning | LWC (or Aura only when required for legacy APIs) |
Existing VF pages that work, PDF generation (renderAs="pdf"), email templates-adjacent HTML, certain console/legacy embeds | Keep VF until migration cost is justified |
| Exam question: “best long-term UI framework for a new component in Lightning Experience” | Usually LWC |
| Exam question: debug standard controller field binding, view state, or extension constructors | Visualforce knowledge |
Bottom line for 11.1: Know apex:page, form + field components, iteration components, {! } + $Resource, and view state awareness. Use VF fluently for legacy and exam scenarios; default new UI design conversations to LWC unless a VF-specific capability (PDF, existing investment) dominates.
A Visualforce page must display and edit fields on a single Account record using Salesforce field-type widgets and submit changes with a Save button. Which markup structure is most appropriate?
Which statement best describes Visualforce view state?
A product owner wants a brand-new interactive UI widget on a Lightning record page composed in App Builder. What should a Platform Developer recommend as the default UI technology?