XF 2.2 Load a xF2 page on <iframe> without blocking / render my own template


Well-known member
Hello all! :)

Well, I need to load a xF2 template page via an iframe, to external sites.

<iframe src="https://www.mydomain.com/xf2/myroute" width="100%" height="500px" border="1" />

This page should not include header / navigation / footer and so on.
Only my own html.

I know that I could call this for my own PAGE_CONTAINER:
<xf:page option="template">MY_CONTAINER</xf:page>

... but it doesn't work. The iframe blocks the loading.

I need on the iframe to load all necessary javascript and css of xF2 and rendering the page.
This means that it should render correctly fields like rating_macros stars_rating etc like stars, not like drop-down menus.

Any idea? :)
You can remove the X-Frame-Options header from the request on your page:

You can remove the X-Frame-Options header from the request on your page:

Thanks for your message! :)

Still not work. It works if I open the page on the browser, but if I open a page with an iframe, it shows this:
Στιγμιότυπο οθόνης 2024-02-17 130720.webp
carousel.html has this:
<iframe src="https://localhost/xf2/carousel" width="100%" height="500px" border="1" />

My code:
    public function actionCarousel(\XF\Mvc\ParameterBag $params)
        $viewParams = [];    
        return $this->view('', 'sc_my_template_raw', $viewParams);

<xf:page option="template">sc_mytemplate</xf:page>

sc_mytemplate: (it is just a PAGE_CONTAINER with header, navigation and footer removed) :
<!DOCTYPE html>
<html id="XF" lang="{$xf.language.language_code}" dir="{$xf.language.text_direction}"
    data-logged-in="{{ $xf.visitor.user_id ? 'true' : 'false' }}"
    data-csrf="{{ csrf_token()|escape('js') }}"
    class="has-no-js {{ $template ? 'template-' . $template : '' }}"
    {{ $xf.runJobs ? ' data-run-jobs=""' : '' }}>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">

    <xf:set var="$siteName" value="{$xf.options.boardTitle}" />
    <xf:set var="$h1"><xf:h1 fallback="{$siteName}" /></xf:set>
    <xf:set var="$description"><xf:description /></xf:set>

    <title><xf:title formatter="%s | %s" fallback="{$xf.options.boardTitle}" page="{$pageNumber}" /></title>

    <link rel="manifest" href="{{ base_url('webmanifest.php') }}">
    <xf:if is="property('metaThemeColor')">
        <meta name="theme-color" content="{{ parse_less_color(property('metaThemeColor')) }}" />

    <meta name="apple-mobile-web-app-title" content="{{ $xf.options.boardShortTitle ?: $xf.options.boardTitle }}">
    <xf:if is="property('publicIconUrl')">
        <link rel="apple-touch-icon" href="{{ base_url(property('publicIconUrl', true)) }}">
    <xf:elseif is="property('publicMetadataLogoUrl')" />
        <link rel="apple-touch-icon" href="{{ base_url(property('publicMetadataLogoUrl')) }}" />

    <xf:foreach loop="$head" value="$headTag">

    <xf:if is="!$head.meta_site_name && $siteName is not empty">
        <xf:macro template="metadata_macros" name="site_name" arg-siteName="{$siteName}" arg-output="{{ true }}" />
    <xf:if is="!$head.meta_type">
        <xf:macro template="metadata_macros" name="type" arg-type="website" arg-output="{{ true }}" />
    <xf:if is="!$head.meta_title">
        <xf:macro template="metadata_macros" name="title" arg-title="{{ page_title() ?: $siteName }}" arg-output="{{ true }}" />
    <xf:if is="!$head.meta_description && $description is not empty && $pageDescriptionMeta">
        <xf:macro template="metadata_macros" name="description" arg-description="{$description}" arg-output="{{ true }}" />
    <xf:if is="!$head.meta_share_url">
        <xf:macro template="metadata_macros" name="share_url" arg-shareUrl="{$xf.fullUri}" arg-output="{{ true }}" />
    <xf:if is="!$head.meta_image_url && property('publicMetadataLogoUrl')">
        <xf:macro template="metadata_macros" name="image_url"
            arg-imageUrl="{{ base_url(property('publicMetadataLogoUrl'), true) }}"
            arg-output="{{ true }}" />

    <xf:macro template="helper_js_global" name="head" arg-app="public" />

    <xf:if is="property('publicFaviconUrl')">
        <link rel="icon" type="image/png" href="{{ base_url(property('publicFaviconUrl'), true) }}" sizes="32x32" />
    <xf:include template="google_analytics" />
<body data-template="{$template}">
<div style="width: 1000px; height: 220px; position:relative; background-color:#3f464e; border-radius:2px;">
    <div id="carousel">
        <div class="slides">
            <xf:if is="$slides is not empty">
                <xf:foreach loop="$slides" value="$review">
                    <div class="slide">
                        <xf:macro template="rating_macros" name="rating"
                            arg-deselectable="{{ false }}"
                            arg-row="{{ false }}"
                            arg-readOnly="{{ true }}" />                    
            <xf:else />
                <div class="slide" style="color: #fff;">Currently no slides!</div>

#carousel {
    width: 620px;
    overflow: hidden;
    position: relative;
    margin-left: 305px;
.slides {
   display: flex;
   transition: transform 0.5s ease;
.slide {
    flex-shrink: 0;
    border: 1px solid red;
    width: calc(50% - 10px);
    height: 110px;
    flex-shrink: 0;
    margin-right: 10px;
    margin-top: 48px;
    let currentIndex = 0;
    const slides = document.querySelectorAll('.slide');
    const totalSlides = slides.length;
    const slideWidth = slides[0].offsetWidth; // Get the width of the first slide

    function nextSlide() {
        currentIndex = (currentIndex + 2) % totalSlides;

    function prevSlide() {
        currentIndex = (currentIndex - 2 + totalSlides) % totalSlides;

    function updateCarousel() {
        const offset = -currentIndex * (slideWidth + 10); // Adjusted for margin
        document.querySelector('.slides').style.transform = `translateX(${offset}px)`;
Sorry, I should have clarified. The default value is set when the renderer is set up, which happens after the controller has executed. You would need to remove it in a view class.
Obviously it's going to be best to do it via code on your server, but if for whatever reason that's not an option, if your site uses Cloudflare, you can use Header Transform Rules to do it.

Top Bottom