Most LTI tools default to iframe. The LMS supports it, the docs show it, and it works — until a real user sits down and tries to navigate your product.
Here's what you get from this post: a clear decision rule for iframe vs new tab, what actually breaks when you get it wrong, and the one technical step most tutorials skip entirely.
What breaks when you use iframe for the wrong tool
When your tool loads inside an LMS iframe, the LMS keeps its own sidebar, top nav, and breadcrumbs visible. Your tool has the same. The user now sees two navigation systems on one screen.
They don't file a bug report. They don't understand what's happening technically. They just get confused about which navigation to use — the LMS one or yours — and quietly give up.
I saw this firsthand when a complete course player rendered inside a Moodle iframe. The tool had its own sections, its own progress nav, its own sidebar. Inside the LMS iframe, it looked broken. Not technically broken — visually broken in a way no amount of CSS fixes.
The problem isn't the code. It's that iframe was never designed to contain a complete product experience.
The decision rule
Use iframe when your tool renders something passive — a file, a static page, a read-only embed. The user isn't navigating. They're consuming. Iframe is fine.
Open in a new tab when your tool is a complete experience with its own navigation, flows, or state. Course players, interactive assessments, multi-step tools — anything where the user needs the full screen to understand what they're doing.
A PDF viewer? Iframe works. A course with content, assignments, and quizzes? New tab. A simple content embed? Iframe is fine. An interactive assessment with progress tracking? New tab.
How to redirect to a new tab in LTI
Technically, the redirect itself is simple. In your tool's launch handler, instead of rendering content in the response, you return a page that immediately opens your full application in a new tab.
The part most tutorials skip: when you redirect out of the iframe, you lose the LTI launch context.
The LMS sends you context once — at launch. It tells you who the user is, which course they came from, which specific activity they're trying to open, and gives you a token your system needs to make sense of all of it. The moment you redirect to a new tab without carrying that forward, your tool opens with no idea who the user is or what they're supposed to see.
Before the redirect, pass the token and any session data your system needs — user ID, activity reference, course context — into the new tab URL or a short-lived session store. Your application in the new tab reads it on load and picks up exactly where the launch left off.
Without this step, the new tab opens but your tool is blind. The user sees a broken state or gets sent to a default page that has nothing to do with what they clicked.
The decision in one place
- Does your tool have its own navigation or multi-step flows? New tab.
- Will users spend more than a couple of minutes inside it? New tab.
- Is it a file, static content, or a simple embed? Iframe is fine.
- Are you redirecting to a new tab? Pass the token before you go.
If you're building anything beyond a simple content viewer, the iframe default is costing you user experience you haven't seen break yet. You will, once real users touch it.
FAQ
Can I use iframe and new tab for different parts of the same tool?
Yes. You can launch the tool in a new tab for the full experience while still using iframe embeds for specific passive content inside your tool. The LTI launch decision is about the initial entry point.
What if the LMS blocks new tab redirects?
Some LMS configurations restrict certain behaviors inside the launch flow. Test your redirect in your target LMS environments — Moodle, Canvas, and Brightspace all handle this slightly differently. If a redirect is blocked, the fallback is to render a simple page with a prominent "Open in full screen" button that the user clicks manually.
What data should I pass when redirecting to a new tab?
At minimum: the user ID, the resource link ID (which activity was launched), and the course context ID. If your system uses a token to look up session data, pass that too. Don't pass raw PII in the URL — use a short-lived server-side session keyed to a token instead.
Top comments (0)