Skip to main content
TrailblazerDX, the ultimate AI learning event is heading back to San Francisco March 6-7, 2024. Register Now and save 35% with code T24DEYE424.

Explore the Visualforce App Container

Learning Objectives

After completing this unit, you’ll be able to:
  • Describe three differences between Visualforce pages running in Salesforce Classic compared to the same pages running in Lightning Experience.
  • Describe two common code patterns that need updating to work in Lightning Experience.
  • List two changes to Visualforce page default values when running in Lightning Experience.

Exploring the Visualforce App Container

The largest difference between Visualforce in Lightning Experience and Visualforce in Salesforce Classic is the environment it runs in. In Salesforce Classic, Visualforce “owns” the page, the request, the environment. Visualforce is the application container. But in Lightning Experience, Visualforce runs inside an iframe that’s wrapped inside the larger Lightning Experience container.

This change to the execution context has a number of effects on the way Visualforce pages can affect the overall Salesforce application. We’ll talk about these changes in this unit, but save the full details of a few of them for their own units.


This unit is a little more “under construction” than the rest. The reason is simple: The impact of the issues described here is highly dependent on your code. We’ve worked really hard to make things “just work” for you, and in most cases little or nothing here will show on your radar. But we can’t anticipate every way that you’re using Visualforce. Here we’re outlining the general aspects of how Lightning Experience affects Visualforce. When you have conversations with us, and as we learn more from you about actual impact, we can offer explanations with more details about how to address specific issues.

The Outer Lightning Experience Container

Let’s start with the outer container, the Lightning Experience application. The Lightning Experience container is a “single-page application,” or SPA, which is accessed at the /lightning URL. The /lightning page loads, its code starts up, and that application code takes over the environment.

The process by which a single-page application loads its resources—usually a static HTML shell and a lot of JavaScript—is both interesting and complex. If you’ve worked with JavaScript frameworks like AngularJS or React, you’re reasonably familiar with the basics of how Lightning Experience, in the form of /lightning, starts up. And to be honest, the full details don’t matter. You don’t have any control over it, and the implementation continues to evolve.

Here’s what’s important to know: Lightning Experience, or /lightning, is in charge of the request. Your Visualforce page is not. Your page needs to work within constraints that Lightning Experience imposes upon it. Lightning Experience is the parent context, and your Visualforce page is the child context. Children need to obey their parents.

Some of these constraints, such as the size of the frame in which your Visualforce page is displayed, are imposed directly by Lightning Experience. They’re easier to understand and work with, and we’ll talk about them in a minute.

Other constraints are implicit, and enforced not by Lightning Experience but by the browser running it. These are mostly security and JavaScript execution constraints. Most pages aren’t impacted by these security constraints, and those that are usually fail early and with clear error messages. JavaScript errors are harder to discover and diagnose, but there are some general rules we’ll cover in a bit.

The Visualforce iframe

When your Visualforce page runs in Lightning Experience, it’s displayed inside an HTML iframe. An iframe creates an embedded browsing context that’s effectively a separate browser “window” from the main Lightning Experience browsing context. The iframe creates a boundary between the Visualforce page and its parent, the Lightning Experience application.

The advantage of running Visualforce pages inside an iframe is that, for pages that don’t need to access or change the top-level browsing context, running inside the iframe looks almost exactly like running as a page in Salesforce Classic. This is why you don’t need to modify all of your Visualforce pages to adapt to the wildly different behind-the-scenes request environment of Lightning Experience. It’s an important part of the “just works” strategy for supporting Visualforce.

Of course, the flip side is that pages that do need to access the top-level browsing context, well, there’s some things that need to change. We’ll cover some specifics in the next section.

If your page is communicating with services besides Salesforce, the iframe boundary might also result in you needing to update your organization’s CORS settings, remote site settings, clickjack settings, or content security policy. Since these depend on security policies and settings outside of Salesforce, we can’t provide a recipe for specific changes. We simply call it to your attention here.

Impact of the New Container

The effects of the new Visualforce container—embedding the Visualforce page into an iframe within the Lightning Experience app—can be broadly divided into two categories, which we’ll call security and scope.

Again, we want to emphasize: many, or even most Visualforce pages won’t be affected by these issues. But for those that are, we’re thinking “forewarned is forearmed.” You’ll find the source of the problem faster if we’ve already talked about it together.

Security Impact

Elements of security that might be affected include the following.
  • Session maintenance and renewal
  • Authentication
  • Cross-domain requests
  • Embedding restrictions

We discussed a few of these briefly already, the items dealing with cross-domain requests. That is, when the content in the full browser window comes from requests to different servers and services, there’s the potential for any of those requests to balk at being displayed in a context that it’s not prepared for. Your mission, should the need arise, is to prepare those services to handle requests intended to be put together within the Lightning Experience context. As we said before, the details vary, so we can’t provide specific answers here.

One thing we do want to mention specifically is session maintenance. A “session” for our purposes here is basically some kind of token that your browser re-uses from request to request so that you don’t need to enter your username and password for every request. You often need to access the current session using the global variable $Api.Session_ID .

Here’s the thing to keep in mind. $Api.Session_ID returns different values depending on the domain of the request. This is because the session ID varies during a session whenever you cross a hostname boundary, such as to Normally Salesforce transparently handles session hand-off between domains, but if you’re passing the session ID around yourself, be aware that you might need to re-access $Api.Session_ID from the right domain to ensure a valid session ID.

Lightning Experience and Visualforce pages are not only held in different browser contexts, they’re also served from different domains. So, even though it’s all showing in one browser window, the session ID inside the Visualforce iframe will be different than the session ID outside the iframe, in another part of Lightning Experience. Salesforce and Lightning Experience handle this transparently in normal use. But if you’re passing around the session ID like hors d'oeuvre at a party (not usually a good idea), you might need to review how you’re handling it.

Scope Impact

When we talk about scope we’re mainly talking about the following kinds of things.
  • DOM access and modification
  • JavaScript scope, visibility, and access
  • JavaScript global variables such as window.location

If this list sounds complicated or confusing, don’t worry, we can boil it down to something simple and easy to remember: Don’t touch someone else’s stuff. Specifically, your JavaScript code (and stylesheet rules, for that matter) can affect elements—DOM nodes, JavaScript variables, and so on—in your page’s browsing context, but it can’t access elements in any other browsing context, like the parent Lightning Experience context. Don’t touch other contexts’ stuff!

Practically speaking, the most common code pattern where you’d want to do this kind of thing is to manipulate window.location to navigate to another page. This is such a common thing to do, we’ve written up details on this specific issue...well, by the time you’re done with this module, you’ll be sick of hearing about it, we promise.

One last note. If you’re an experienced JavaScript developer, you’re probably already thinking you know how to deal with “I don’t have access to the parent browsing context” issues, by using contentWindow, window.parent, or the like. Please don’t. You’ll likely run afoul of the same-origin policy (Visualforce and Lightning Experience are served from different domains, remember?). Even if you don’t, you’re probably replacing obvious, blocking bugs with subtle, intermittent bugs. Where do you want to spend your time: Doing things right, or the debugger?

Doing things right means calling APIs we’ve made available in your Visualforce pages, primarily for navigation. If you really need to affect things across frame boundaries, use window.postMessage to send a message to receiving code in the other frame.

Visualforce Defaults and Environment Changes in Lightning Experience

When your Visualforce pages run in Lightning Experience a number of low-level changes happen behind the scenes. These changes enable most pages to “just work” in the Lightning Experience container, and sometimes you can just be happy they’re there. But you’ll still want to know they’re happening, especially when you’re working on advanced application flows, or troubleshooting a tricky problem.

Some of these changes are simple, and obvious once you think about them. For example, Visualforce pages that run in Lightning Experience always have the standard Salesforce Classic header and sidebar suppressed. Other changes aren’t as visible, but have just as large an impact.

<apex:page> showHeader and sidebar Attributes Are Always false

These attributes affect the Salesforce Classic header and sidebar on Visualforce pages. The Salesforce Classic header and sidebar are always suppressed when pages run in Lightning Experience, in favor of Lightning Experience navigation elements. There are no corresponding attributes to affect the Lightning Experience header or sidebar because they can’t be suppressed.

If your page is shared between Salesforce Classic and Lightning Experience, you can still set these attributes to the values you’d like to use when the page runs in Salesforce Classic.


The standardStylesheets attribute of <apex:page>, which determines whether to include or suppress the standard Salesforce Classic stylesheets, is unaffected by Lightning Experience. That is, it defaults to true in Lightning Experience, but you’re able to change it.

The JavaScript Utility Object

Although sounds like a droid working in the Salesforce cantina, * it’s actually a utility object that provides a number of useful functions you can use in your own JavaScript code. is automatically injected into your page when it runs in Lightning Experience or the Salesforce app. You’ll see it in your JavaScript debugger console and web developer resources list. There’s nothing you need to do to add it, and there’s no way to suppress it, either. (Sadly, there’s no way to get in your Visualforce pages in Salesforce Classic.) is primarily used to fire navigation events. The full details are in an upcoming unit, Managing Navigation .


* There is no Salesforce cantina. Alas.

Keep learning for
Sign up for an account to continue.
What’s in it for you?
  • Get personalized recommendations for your career goals
  • Practice your skills with hands-on challenges and quizzes
  • Track and share your progress with employers
  • Connect to mentorship and career opportunities