Cannot get my forked frontend‑component‑footer to show in Tutor dev – changes invisible & no build‑mounts

Hola a todos,

Estoy intentando reemplazar el pie de página predeterminado con mi propia versión de frontend-component-footer , pero ningún cambio que hago aparece en Tutor dev: solo veo el logotipo predeterminado y tutor mounts list el build-mount permanece vacío. A continuación se muestra todo lo que he probado.

ENTORNO
• Tutor 19.0.2 (Indigo)
• WSL 2 – Ubuntu 22.04
• Repositorio de bifurcación → GitHub - Edwin75206/frontend-component-footer: Componente de pie de página para Academus (rama master)
• Complemento personalizado → tutor‑footer‑fork 0.1.0

PLUGIN (footer_fork/plugin.py)

from tutor import hooks
from tutormfe.hooks import MFE_APPS

hooks.Plugin(name="tutor-footer-fork", version="0.1.0")

@MFE_APPS.add()
def replace_footer(mfes):
    mfes["frontend-component-footer"] = {
        "repository": "https://github.com/Edwin75206/frontend-component-footer.git",
        "version":    "master",
        "port":       8080     # also tested 1997
    }
    return mfes

Instalado con pip install -e . ; El tutor lo muestra en tutor plugins list .

LO QUE HE HECHO

  1. guardar configuración del tutor
  2. Lanzamiento de tutor dev
  3. tutor dev start mfe → no aparece la línea “Proxying frontend-component-footer …”
  4. Registros de desarrollo del tutor mfe → solo inicio de Caddy, nada sobre el pie de página
  5. Dentro del contenedor tutor_dev-mfe-1 → ss -ltn | grep 8080 no devuelve nada
  6. Se ejecutó npm start manualmente en el contenedor → Webpack compila y escucha, pero las solicitudes a apps.local.openedx.io:1995/frontend-component-footer aún devuelven el estado ":0
  7. Probé el puerto 1997 plus npm start -- --host 0.0.0.0 --port 1997 → mismo resultado
  8. Limpié el caché, reconstruí las imágenes (las imágenes del tutor crean mfe), reinicié los servicios, sin suerte.

CAMBIOS DE CÓDIGO QUE NUNCA APARECEN
En src/components/Footer.jsx agregué:

<span style={{ color: 'orange' }}>⚡ footer dev ⚡</span>

Al ejecutar npm start fuera de Tutor, se muestra el texto naranja en http://localhost:8080 , pero dentro de Tutor ( http://apps.local.openedx.io:1995/frontend-component-footer ) todavía veo solo el logotipo.

ENTRADA DE MONTURA VACÍA
La lista de monturas del tutor muestra:

- name: /home/edwin/frontend-component-footer
  build_mounts: []
  compose_mounts: []

No hay diferencia alguna al volver a agregar el montaje con o sin una cadena de contexto.

COSAS QUE SÍ FUNCIONAN
• Otras bifurcaciones (bifurcación de panel de aprendizaje, bifurcación de aprendizaje) se crean correctamente.
• La bifurcación de pie de página se compila correctamente cuando se inicia manualmente.

PREGUNTAS

  1. ¿Cuál es la forma correcta de hacer que Tutor dev clone mi bifurcación e inicie automáticamente el servidor webpack-dev-server?
  2. ¿Por qué MFE_APPS no crea la entrada build_mounts o la línea proxy para este MFE?
  3. ¿Sería mejor utilizar frontend-slot-footer para realizar pequeños ajustes y, de ser así, cómo integrarlo con Tutor?
  4. ¿Me estoy perdiendo algún paso para que los cambios de código aparezcan realmente dentro de Tutor?

Muchas gracias de antemano por cualquier orientación. He estado estancado en esto durante días.

u can HIDE, Insert, Modify footer and header with plugins slots. Very convenient

Use A Frontend Plugin Framework Slot — Latest documentation

1 Like

Did you try rebuilding mfe image? GitHub - overhangio/tutor-mfe: This plugin makes it possible to easily add micro frontend (MFE) applications on top of an Open edX platform that runs with Tutor.

It was an option, but I feel that editing the plugin directly is better, or how does the slot work better?

Yes, when reconstructing the images, I first saved, reconstructed and launched tutor local launch, but it still doesn’t work

If you fork a component, upgrading becomes much more difficult. Using slots you can be sure that when you upgrade, even if the component has changed, the slot and your changes will still exist. We strongly recommend using slots.

You can try install this plugin

pomegranited/tutor-contrib-enable-feature-flag: Demonstrates how to add a feature flag to Tutor

and add in the plugin.py

PLUGIN_SLOTS.add_items([
  (
    "all",
    "learning_logged_out_items_slot",
    """
    {
      op: PLUGIN_OPERATIONS.Modify,
      widgetId: 'default_contents',
      fn: (widget) => {
        widget.content.buttonsInfo = [
          {
            href: 'https://docs.openedx.org/en/latest/',
            message: 'Documentation'
          },
          {
            href: 'https://discuss.openedx.org/',
            message: 'Forums'
          },
          {
            href: 'https://openedx.org/',
            message: 'openedx.org',
            variant: 'primary'
          }
        ];
        return widget;
      }
    }
    """
  )
]);

PLUGIN_SLOTS.add_items([
    # Hide the default footer
    (
        "all",
        "footer_slot",
        """
        {
          op: PLUGIN_OPERATIONS.Hide,
          widgetId: 'default_contents',
        }"""
    ),
    # Insert a custom footer
    (
        "all",
        "footer_slot",
        """
        {
          op: PLUGIN_OPERATIONS.Insert,
          widget: {
            id: 'custom_footer',
            type: DIRECT_PLUGIN,
            RenderWidget: () => (
              <h1>This is the footer.</h1>
            ),
          },
        }"""
    )
])

There might be issues if you have the Indigo plugin enabled, and so far I haven’t found a solution. But this doesn’t depend on whether you made changes directly or through plugin slots — a conflict occurs between MFE and Indigo.

Hello Sariona , I’m currently working with the frontend-component-footer repo and I noticed that the FooterSlot has already been implemented with the Slot ID: org.openedx.frontend.layout.footer.v1. The documentation says I can hide or replace the default footer using an env.config.jsx file, but I have a few questions:

  1. Does the env.config.jsx file already exist by default in any MFE (like frontend-app-learning or frontend-app-learner-dashboard)?
  2. If not, should I create it manually in the root of the MFE?
  3. What is the correct structure of that file to insert a custom component into the FooterSlot?

I’m using Tutor and have the MFE bind-mounted in dev mode with tutor mounts add and tutor dev launch, so I’m looking to apply this customization without editing the core MFE files.

Any guidance or example would be greatly appreciated. :folded_hands:

Hi @Edwin75206 - can you review this documentation and let me know what is missing? Use A Frontend Plugin Framework Slot — Latest documentation

Yes, I’ve reviewed it, but I’ve already installed the slots and they’re not working. I’m not sure if my architecture is correct or how they’re supposed to be installed. The documentation only shows how to use them, but not how to install them or how they appear in production.

So for me to use the Footer, should I use it as a Plugin or component? It just doesn’t let me edit it

Can you share the steps you’re taking to try to edit the footer using a slot?

tutor-indigo/
└── plugins/
└── footer-slot-fork/
├── footer_slot_fork/
│ └── plugin.py
├── patches/
│ └── mfe/
│ └── env.config.jsx
├── setup.py
└── pyproject.toml
I tried installing it as a plugin, but I’m not sure if I did it correctly. Can you please tell me the correct way to install and activate a plugin like this in Tutor?
What I really want to understand is how I should properly install or structure my code. I don’t know exactly where the env.config.jsx file should go.

With other MFEs like frontend-app-learner-dashboard, I was able to make modifications successfully. What I did there was:

  1. Fork the original Open edX repo.
  2. Clone that fork into my local machine.
  3. Create a plugin so that Tutor stops pointing to the official Open edX GitHub repo and instead points to my fork.
  4. Run tutor mounts add to bind-mount my local fork.
  5. Then I run tutor config save, tutor images build mfe, and finally tutor local launch.

With that process, the changes were applied and worked.

However, it didn’t work for the footer. I tried using the frontend-component-footer repo, but I couldn’t get it to work either. So I moved on to using plugin slots, but that also didn’t work. I even cloned the frontend-slot-footer repo, but still no success.

So what I really want to know is: how should I do it? The documentation just says to add an env.config.jsx file, but it doesn’t explain how to install it properly, what the correct folder structure is, or where exactly that file should go.

If someone could guide me or has any ideas, I’d really appreciate it.

Alright, everything you did is incorrect. You should not add anything to the Indigo plugin to activate the slots that edit the header and footer.

You need to create a custom plugin. You can read about it here:

But let me explain step-by-step how to use the slots for now:

1. Install the plugin enable-feature-flag:
1.1 pip install git+https://github.com/pomegranited/tutor-contrib-enable-feature-flag
1.2 tutor plugins enable enable-feature-flag
1.3 tutor local launch

2. Add the following inside this plugin in the plugin.py file:
2.1 nano tutorenable_feature_flag/plugin.py
2.2 Add the following code:

from tutormfe.hooks import PLUGIN_SLOTS
PLUGIN_SLOTS.add_items([
  (
    "all",
    "learning_logged_out_items_slot",
    """
    {
      op: PLUGIN_OPERATIONS.Modify,
      widgetId: 'default_contents',
      fn: (widget) => {
        widget.content.buttonsInfo = [
          {
            href: 'https://docs.openedx.org/en/latest/',
            message: 'Documentation'
          },
          {
            href: 'https://discuss.openedx.org/',
            message: 'Forums'
          },
          {
            href: 'https://openedx.org/',
            message: 'openedx.org',
            variant: 'primary'
          }
        ];
        return widget;
      }
    }
    """
  )
])

PLUGIN_SLOTS.add_items([
    # Hide the default footer
    (
        "all",
        "footer_slot",
        """
        {
          op: PLUGIN_OPERATIONS.Hide,
          widgetId: 'default_contents',
        }"""
    ),
    # Insert a custom footer
    (
        "all",
        "footer_slot",
        """
        {
          op: PLUGIN_OPERATIONS.Insert,
          widget: {
            id: 'custom_footer',
            type: DIRECT_PLUGIN,
            RenderWidget: () => (
              <h1>This is the footer.</h1>
            ),
          },
        }"""
    )
])

2.3 Save the file and run:
tutor config save

3. Then you can rebuild the images using the command:
tutor images build mfe openedx
(If it doesn’t work right away, try running tutor local launch first, then tutor images build mfe openedx.)

You can find the slot for each element in the header here:

For the footer, check here:

In the same way, you can edit the dashboard page using slots:

There may be other pages you can customize using slots, but I’m not entirely sure. You can try searching online for more information.

Note: There may be some bugs when using the Indigo plugin. I recommend disabling it. In any case, you can try both options—with Indigo enabled and disabled—and let me know if you run into any issues.

1 Like

Thanks—that really worked: it removed the footer, and I did disable the indigo theme, but now I have the problem that my favicon and logo have been replaced by the defaults. I have two questions:

  1. To keep adding slot modifications, should I do it directly here:
    /home/edwin/tutor-env/lib/python3.12/site-packages/tutorenable_feature_flag/plugin.py?
    This is an example of the change I made.
from tutormfe.hooks import PLUGIN_SLOTS
PLUGIN_SLOTS.add_items([
  (
    "all",
    "learning_logged_out_items_slot",
    """
    {
      op: PLUGIN_OPERATIONS.Modify,
      widgetId: 'default_contents',
      fn: (widget) => {
        widget.content.buttonsInfo = [
          {
            href: 'https://docs.openedx.org/en/latest/',
            message: 'Documentation'
          },
          {
            href: 'https://discuss.openedx.org/',
            message: 'Forums'
          },
          {
            href: 'https://openedx.org/',
            message: 'openedx.org',
            variant: 'primary'
          }
        ];
        return widget;
      }
    }
    """
  )
]);

PLUGIN_SLOTS.add_items([
  // Hide the default footer
  (
    "all",
    "footer_slot",
    """
    {
      op: PLUGIN_OPERATIONS.Hide,
      widgetId: 'default_contents',
    }
    """
  ),
  // Insert a custom footer
  (
    "all",
    "footer_slot",
    `
    {
      op: PLUGIN_OPERATIONS.Insert,
      widget: {
        id: 'custom_footer',
        type: DIRECT_PLUGIN,
        RenderWidget: () => (
          <footer style={{
            borderTop: '1px solid #ccc',
            padding: '1rem 2rem',
            display: 'grid',
            gridTemplateColumns: '1fr auto',
            gridTemplateRows: 'auto auto',
            alignItems: 'center',
            fontSize: '0.9rem',
            gap: '0.5rem'
          }}>
            <div style={{ gridRow: '1 / span 2' }}>
              © 2025 Academus Digital. All rights reserved.
            </div>
            <a href="/contact" style={{ textDecoration: 'none', justifySelf: 'end' }}>
              Contact
            </a>
            <a href="/about" style={{ textDecoration: 'none', justifySelf: 'end' }}>
              About
            </a>
          </footer>
        ),
      },
    }
    `
  )
]);

PLUGIN_SLOTS.add_items([
  (
    "all",
    "logo_slot",
    `
    {
      keepDefault: false,
      plugins: [
        {
          op: PLUGIN_OPERATIONS.Insert,
          widget: {
            id: 'custom_logo_component',
            type: DIRECT_PLUGIN,
            RenderWidget: () => (
              <a href="/" style={{ display: 'inline-block' }}>
                <img
                  src="./resources/studio-logo.png"
                  alt="Academus Digital"
                  style={{ height: '40px' }}
                />
              </a>
            ),
          },
        },
      ],
    }
    `
  )
]);

I tried adding the file in that same location to see if it would modify the header, but it didn’t work. To change those elements, do I need to create a separate plugin or can I add the modifications there as well?

I’m not sure if this is the correct way to do it, because I haven’t found any official instructions or guides online. But what I do is fork the edx-platform, then add a link to my fork in config.yml. After that, I replace favicon.ico and logo.png in:

Then, I build the images using:

tutor images build mfe openedx

I’ve also encountered an issue where, after disabling the Indigo theme, some of its elements still remain, even though it seems to be disabled. In that case, try the following:

tutor local exec --user root lms bash
cd /openedx/themes
rm -rf "indigo"

Then rebuild all the images again:

tutor images build mfe openedx

I think in your case the code is incorrect. I don’t write code in JS, especially not in React, so I can’t say for sure. But in any case, you can add all the slots in tutorenable_feature_flag/plugin.py.

Example:

PLUGIN_SLOTS.add_items([
  (
    "all",
    "learning_logged_out_items_slot",
    """
    {
      op: PLUGIN_OPERATIONS.Modify,
      widgetId: 'default_contents',
      fn: (widget) => {
        widget.content.buttonsInfo = [
          {
            href: 'https://docs.openedx.org/en/latest/',
            message: 'Documentation'
          },
          {
            href: 'https://discuss.openedx.org/',
            message: 'Forums'
          },
          {
            href: 'https://openedx.org/',
            message: 'openedx.org',
            variant: 'primary'
          }
        ];
        return widget;
      }
    }
    """
  )
]);

PLUGIN_SLOTS.add_items([
  # Hide the default footer
  (
    "all",
    "footer_slot",
    """
    {
      op: PLUGIN_OPERATIONS.Hide,
      widgetId: 'default_contents',
    }"""
  ),
  # Insert a custom footer
  (
    "all",
    "footer_slot",
    """
    {
      op: PLUGIN_OPERATIONS.Insert,
      widget: {
        id: 'custom_footer',
        type: DIRECT_PLUGIN,
        RenderWidget: () => (
          <h1>This is the footer.</h1>
        ),
      },
    }"""
  )
]);

PLUGIN_SLOTS.add_items([
  (
    "all",
    "logo_slot",
    """
    {
      op: PLUGIN_OPERATIONS.Hide,
      widgetId: 'default_contents',
    }"""
  ),
  (
    "all",
    "logo_slot",
    """
    {
      op: PLUGIN_OPERATIONS.Insert,
      widget: {
        id: 'custom_logo_component',
        type: DIRECT_PLUGIN,
        RenderWidget: () => (
          <h1 style={{textAlign: 'center'}}>🗺️</h1>
        ),
      },
    }"""
  )
]);

This will apply three changes (footer, logo, and header when logged out).
For the logo_slot, I recommend not modifying it directly, but just implementing it as shown above. That way, you’ll have a unified logo across all pages.

Edit:
In any case, you should try changing the logo_slot to make sure it works correctly.