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
Last updated: August 2026

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:form around 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:

AttributeRole
standardControllerName of the standard or custom object (for example, Account)—provides the record context and standard actions
extensionsComma-separated Apex classes that extend the standard (or custom) controller
controllerApex class name for a custom controller (no standard controller)
recordSetVarVariable name for the list of records when using a standard list controller
actionMethod run before the page renders (for example, init logic)
renderAsOften pdf for PDF generation
sidebar / showHeader / applyHtmlTag / applyBodyTagChrome and HTML shell control—especially relevant when embedding
docTypeHTML doctype when you need modern HTML/CSS behavior
lightningStylesheetsApplies 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 controller and standardController on the same page as peers in the “pick one primary controller” sense used on the exam: custom controller or standard controller (+ optional extensions).
  • extensions require a constructor that accepts the standard (or custom) controller type.
  • Without apex:form, postback components like apex:commandButton cannot 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 when standardController="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

ComponentPurpose
apex:pageBlockSalesforce-styled block with title
apex:pageBlockSectionMulti-column section inside a pageBlock
apex:pageBlockSectionItemLabel/value pair control when not using inputField/outputField auto-layout
apex:pageBlockButtonsButton bar (top/bottom of pageBlock)
apex:pageBlockTableTable bound to a list; Salesforce styling
apex:sectionHeaderTitle/subtitle header
apex:tabPanel / apex:tabTabbed UI

Forms and fields

ComponentPurpose
apex:formRequired container for postbacks and view state
apex:inputFieldRenders the correct editor for the field type and enforces FLS presentation
apex:outputFieldRead-only field display with correct type formatting
apex:inputText / inputTextarea / inputCheckbox / selectListGeneric inputs when not binding typed sObject fields
apex:commandButton / commandLinkSubmit form; action points to controller method
apex:actionSupport / actionFunction / actionPoller / actionStatusPartial-page AJAX behaviors
apex:message / apex:messages / apex:pageMessagesValidation 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>
ComponentWhen to choose
pageBlockTableStandard Salesforce table look inside pageBlocks
dataTableHTML table with more control; still server-side iteration
repeatFull 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:

  1. Inline styles/scripts for tiny demos—avoid for production; hard to cache and reuse.
  2. Static resources for CSS, JS libraries, fonts, and images (single files or zips).
  3. apex:includeScript / apex:stylesheet so dependencies load in a controlled order.
  4. JavaScript remoting (@RemoteAction) or actionFunction when 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

TechniqueEffect
Mark large controller properties transientExcluded from view state; re-query or rebuild each request
Reduce rows shown; paginate (StandardSetController)Smaller component/state graph
Prefer outputField / fewer input components when possibleLess form state
Avoid stuffing huge Maps/Lists into non-transient propertiesDirect size cut
Use JavaScript remoting / redesign for partial patternsCan avoid full classic view-state postbacks for some interactions
Split complex UIs into multiple pages or Lightning componentsArchitectural 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

SituationLean toward
New interactive record UI, App Builder composition, mobile-friendly LightningLWC (or Aura only when required for legacy APIs)
Existing VF pages that work, PDF generation (renderAs="pdf"), email templates-adjacent HTML, certain console/legacy embedsKeep 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 constructorsVisualforce 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.

Test Your Knowledge

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?

A
B
C
D
Test Your Knowledge

Which statement best describes Visualforce view state?

A
B
C
D
Test Your Knowledge

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?

A
B
C
D