Visualforce Components
Visualforce Components are reusable UI building blocks in the Visualforce framework.
Definition
Visualforce Components are reusable UI building blocks in the Visualforce framework. Each component is a custom-defined tag (like <c:myButton>) that encapsulates markup, attributes, and an optional Apex controller, letting developers build a library of consistent UI pieces that can be reused across multiple Visualforce pages. The Visualforce Components Setup page lists every custom component in the org with its name, namespace, API version, and last-modified timestamp.
The concept predates Lightning Web Components by about a decade and was the primary path to component-based UI on Salesforce until LWC arrived in 2019. Even in modern orgs that have largely moved to LWC, Visualforce Components remain useful for legacy pages, packaged apps that still ship Visualforce, email templates that use Visualforce, and PDF generation scenarios where Visualforce remains the supported path. Understanding how components work is still relevant for any developer maintaining a long-running Salesforce org.
The anatomy of a Visualforce Component
Component markup and attributes
A Visualforce Component is defined in an .component file containing Visualforce markup wrapped in an <apex:component> root tag. The component declares attributes via <apex:attribute>, which appear as named parameters when the component is consumed. Each attribute has a type, a description, an assignTo target (the controller property it sets), and a required flag. The body of the component is regular Visualforce markup that can include other tags, expressions ({!attributeName}), and nested components. When a page uses the component via <c:componentName attribute1=$t$value$t$ attribute2=$t${!record.Name}$t$ />, the framework substitutes the attribute values into the component's body and renders the result.
Optional Apex controller for logic
Components can declare an Apex controller via the controller attribute on <apex:component>. The controller is a regular Apex class that exposes properties and methods the component's markup references. Component controllers are instantiated once per component instance per page render, so a page that uses the same component three times instantiates the controller three separate times. The controller handles computation, data fetching, and any logic too complex for inline expressions. Action methods on the controller can be wired to <apex:commandButton> and similar action tags inside the component, supporting interactive behavior within the component scope.
Composition and reuse patterns
Component composition is what makes Visualforce Components useful. A common pattern is to build small atomic components (a styled button, a labeled field, a confirmation dialog) and compose them into larger components (a form section, a record header). Pages then consume the high-level components, getting the consistent styling and behavior of the atomic pieces for free. The <apex:componentBody> tag inside a component lets the parent component slot in arbitrary markup from the consumer, similar to slots in LWC. This pattern is essential for components that need to wrap conditional or templated content rather than just substitute attribute values.
Namespacing: c versus apex versus custom
Visualforce Components live in a namespace. Standard components from Salesforce use the apex: namespace (<apex:pageBlock>, <apex:inputField>). Custom components in the org's default namespace use the c: namespace (<c:myComponent>). Components from managed packages use the package's namespace prefix (<acme:packageComponent>). The namespace is part of the tag name and cannot be omitted. When an org has multiple custom components with the same local name from different packages, the namespace prefix disambiguates them. The Visualforce Components Setup page shows the namespace alongside the name, which helps when looking up a component referenced in a page.
Components in email templates and PDF rendering
Two scenarios keep Visualforce Components relevant even in LWC-heavy orgs. Visualforce email templates render HTML emails with merge fields, conditional logic, and rich formatting that the simpler Lightning email template builder cannot match. Custom components inside these templates produce consistent branded headers, signatures, and section blocks across all the org's transactional email. PDF generation is the second scenario: Salesforce renders Visualforce pages to PDF using the renderAs="pdf" attribute, which is the standard path for documents like quotes, invoices, and order confirmations. Custom components inside PDF-rendered pages give the same reusability benefits as in regular pages.
Lifecycle and reactivity
Visualforce Components use a server-side rendering model. Every page interaction (button click, dropdown change, form submit) triggers a round-trip to the server where the page (and its components) re-render. The component's controller state is reconstructed from the form state and any view-state data. This is fundamentally different from LWC's client-side reactivity, where DOM updates happen in the browser without server round-trips. The trade-off: Visualforce is simpler to reason about for back-end developers and supports server-side composition easily, but feels slower than LWC for highly interactive UIs. Knowing this trade-off is what guides decisions about which technology to use for which kind of page.
Components and security
Visualforce Components inherit the security context of the page that uses them. By default, the page and its components run in "with sharing" or "without sharing" mode based on the controller class declaration. Field-level security is enforced for inputField and outputField tags that reference standard or custom fields. CRUD permissions are enforced for any DML the controller performs. Cross-site scripting protection is on by default for expressions, so {!userInput} renders escaped HTML automatically. Disabling escape="false" on an outputText tag bypasses this protection, which is occasionally necessary for legitimate HTML but is also the source of most Visualforce XSS vulnerabilities.
Migration patterns from Visualforce to LWC
Most orgs eventually migrate Visualforce Components to LWC. The migration is not automatic, but several patterns make it manageable. The hybrid approach: keep the existing Visualforce page and embed an LWC inside it using <c:lightningOut> or the lightning__OutVf attribute, allowing piecemeal modernization. The wrapper approach: build an LWC equivalent of the Visualforce Component and replace usage one consumer at a time, retiring the Visualforce version when no consumers remain. The full rewrite: redesign the page in LWC from scratch using Lightning App Builder for layout. Choosing among these depends on how interactive the existing component is, how many pages consume it, and how much value the rewrite adds versus the cost. Components used in email templates and PDF rendering should stay in Visualforce because LWC does not yet replace either scenario.
Build and use a Visualforce Component
Creating a Visualforce Component is a four-step flow: design the component interface, implement the markup and controller, register it in the org, and consume it from pages or email templates. The walkthrough below builds a reusable styled-button component as a concrete example.
- Design the component interface
Decide what the component does and what attributes the consumer passes in. For a styled button component, the attributes might be label, action, style class, and disabled flag. Sketch the markup and consider where the body of the component will plug in (the click handler, the label text). Document the design in a short comment block at the top of the component file so future maintainers know the intent and constraints.
- Implement the component markup and controller
From Setup, open Visualforce Components and click New. Provide a name (StyledButton, no spaces), label (Styled Button), and the API version. In the markup, define <apex:component> with attribute tags for each parameter. Inside the component body, use <apex:commandButton> with attribute values bound to the component attributes. If the component needs server-side logic, create a separate Apex class as the controller and reference it on the <apex:component> tag. Save and check for syntax errors.
- Test the component in a Visualforce page
Create a test Visualforce page that consumes the component: <apex:page><c:StyledButton label=$t$Save$t$ action=$t${!save}$t$ /></apex:page>. Verify the component renders, the click handler fires, and the styling is correct. Test edge cases: missing required attributes (should error), styled-class overrides, and disabled state. Iterate the component until it handles every expected case. Add unit tests for the controller class if one exists, targeting at least 75% coverage as required by the platform for production deployment.
- Promote the component to production
Deploy the component (and any controller class) through the standard pipeline: Change Sets for legacy orgs, SFDX or DevOps Center for modern setups. After deployment, update any Visualforce pages or email templates that should consume the new component. Document the component in the org's component library or wiki so other developers know it exists. Future Visualforce work in the org should reuse the component rather than rebuilding the same styling and behavior inline.
The unique name of the component, used in the <c:ComponentName> tag when consumed.
The human-readable label that appears in the Setup list and the Visualforce editor.
The Visualforce content of the component, wrapped in <apex:component> tags.
Required to create and edit Visualforce Components in Setup.
Required only if the component has server-side logic. Otherwise the component is markup-only and renders faster.
- Component attributes are required by default unless required="false". Forgetting this on an optional attribute causes consumers to error on instantiation.
- Each component instance has its own controller instance. A page using the same component three times has three controller objects, which affects shared state and view-state size.
- Visualforce Components do not work inside Lightning Experience natively. They render only inside Visualforce pages, which can themselves be embedded in Lightning via iframe, but the component contract is Visualforce-bound.
- Disabling escape="false" on outputText opens an XSS vulnerability unless the source data is rigorously sanitized. Default escaping is on for a reason.
- Component view-state contributes to the page's total view-state size, which is capped at 170 KB. Heavy components used multiple times can hit the limit unexpectedly.
Trust & references
Cross-checked against the following references.
- Visualforce Component ReferenceSalesforce Developer Docs
- Custom Visualforce ComponentsSalesforce Developer Docs
Straight from the source - Salesforce's reference material on Visualforce Components.
- Custom ComponentsSalesforce Developer Docs
- Render Visualforce as PDFSalesforce Developer Docs
Hands-on resources to go deeper on Visualforce Components.
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 skill set is typically needed to work with Visualforce Components?
Q2. What is a Governor Limit in the context of Visualforce Components?
Q3. Where would a developer typically work with Visualforce Components?
Discussion
Loading discussion…