On this page
Templates (.phtml)¶
Using JavaScript and Vue Components in .phtml Templates¶
With MageObsidian Components, integrating JavaScript files and Vue components into .phtml templates is made simpler and more powerful through the MageObsidian\ModernFrontend\Block\Template class.
This enhanced block class provides methods to efficiently load, resolve, and render resources, maintaining consistency with the import conventions explained earlier. Importing Between Magento Modules
Why Use MageObsidian\ModernFrontend\Block\Template?¶
This block class extends Magento's Template class and introduces powerful new methods, including:
- Dynamic resource resolution: Supports the same
Vendor_Module::notation for resolving file paths. - Simplified Vue integration: Dynamically renders Vue components with properties.
- Streamlined library access: Easily include library files or generated assets via Vite.
Key Features¶
1. Resolving File Paths¶
Files can be resolved dynamically using the resolvePathByName() method. This method allows you to reference files with the Vendor_Module:: notation or directly from themes using Theme::.
Example:
// Resolve a JavaScript file
$fileUrl = $block->resolvePathByName('Vendor_Module::js/main');
// Resolve a Vue component
$componentPath = $block->resolvePathByName('Vendor_Module::components/NavBar');
// Resolve a file from the theme
$themeFile = $block->resolvePathByName('Theme::custom-component');
2. Rendering Vue Components¶
The renderVueComponent() method mounts a Vue component as an island. Instead of an inline <script> per call, it emits a small, inert marker that a single page-level bootstrap discovers and mounts. By default the island hydrates lazily — only when it scrolls into the viewport — so below-the-fold components cost nothing until they are actually needed.
Signature
$block->renderVueComponent(
string $componentName,
array $props = [],
bool $eager = false,
string $serverHtml = '',
bool $hydrate = false
): string;
| Parameter | Description |
|---|---|
$componentName |
Component in Vendor_Module::Component notation (the components/ prefix is implied). |
$props |
Data passed to the component as Vue props. Encoded as attribute-safe JSON; an un-encodable value throws instead of emitting broken markup. |
$eager |
false (default) hydrates when the marker becomes visible; true mounts immediately — use it for above-the-fold components. |
$serverHtml |
Markup rendered inside the marker, so it paints with the document instead of after the component's chunk arrives. |
$hydrate |
true means $serverHtml is the component's initial state and Vue adopts it in place. false (default) makes it a placeholder Vue replaces on mount. |
Example: Rendering a Vue Component in a .phtml Template
<?= $block->renderVueComponent(
'Vendor_Module::NavBar',
[
'title' => 'Welcome',
'userId' => 123
]
) ?>
The above emits a single marker — no inline mount script:
Rendered Output:
<div data-mage-island
data-component="https://magento.test/static/version1733144244/frontend/Vendor/theme/en_US/generated/Vendor_Module/components/NavBar.js"
data-props="{"title":"Welcome","userId":123}"
data-strategy="visible"></div>
For an above-the-fold component that should not wait for the viewport, opt into eager mounting:
<?= $block->renderVueComponent('Vendor_Module::Hero', [], true) ?>
An eager island above the fold should not leave its container empty — that is a hole in the page until its chunk arrives. Render its initial state on the server and let Vue hydrate it:
<?= $block->renderVueComponent('Vendor_Module::Hero', $props, true, $serverHtml, true) ?>
Note: The Vue runtime and the i18n plugin load once per page and are shared across every island; a page with no islands never loads Vue at all. See Vue Islands for the full architecture, the
visible/eagerstrategies and how to generate$serverHtml.
3. Loading Library Files¶
The getViewLibFileUrl() method allows you to include library files conveniently. For example, you can load a specific library script:
Example:
<script type="module" src="<?= $block->getViewLibFileUrl('some-library') ?>"></script>
This ensures consistency with your Vite-based build process.
4. Vite-Generated Files¶
Use the getViteFileUrl() method to load files generated by Vite, such as entry scripts or assets.
Example:
<script type="module" src="<?= $block->getViteFileUrl('Vendor_Module::main') ?>"></script>
Full Example in a .phtml Template¶
Here’s a complete example demonstrating multiple use cases:
<?php
/** @var MageObsidian\ModernFrontend\Block\Template $block */
?>
<!-- Include a JavaScript library -->
<script type="module" src="<?= $block->getViewLibFileUrl('some-library') ?>"></script>
<!-- Load a Vite-generated entry file -->
<script type="module" src="<?= $block->getViteFileUrl('Vendor_Module::main') ?>"></script>
<!-- Render a Vue component dynamically -->
<?= $block->renderVueComponent('Vendor_Module::NavBar', ['title' => 'Welcome to ModernFrontend', 'theme' => 'dark']) ?>
<!-- Resolve a file from the theme -->
<script type="module" src="<?= $block->resolvePathByName('Theme::custom-script') ?>"></script>
Best Practices¶
-
Avoid Hardcoding File Paths
Do not hardcode file paths. Instead, use the block methods likeresolvePathByName(),getViteFileUrl()orgetViewLibFileUrl()for consistent file resolution. -
Pass Props to Vue Components
When rendering Vue components, pass only the necessary data aspropsto keep your components flexible and reusable. -
Keep Templates Clean
Use the block methods to encapsulate logic, keeping your.phtmltemplates focused on structure and presentation.
Benefits of Using This Approach¶
-
Dynamic Resolution
Files and components are resolved dynamically, respecting theme overrides. -
Consistency
Maintains a consistent approach to file handling across JavaScript, Vue components, and templates. -
Future-Proof
By leveraging Vite and a modern block class, your integration is optimized for scalability and performance. -
Ease of Use
Simplifies the process of including scripts and components, reducing boilerplate and errors.
Next Steps¶
- Learn more about overriding components and scripts in the Themes section.
- Read Vue Islands to understand lazy hydration and the
visible/eagerstrategies.
Writing Vue.js Logic Directly in .phtml Templates¶
This section demonstrates how to integrate Vue.js logic directly within a .phtml file. It also showcases how PHP-rendered data can work seamlessly with Vue.js interactivity without creating a separate Single File Component (SFC).
Tip: This inline pattern is handy for a tiny, one-off enhancement on a server-rendered page. For anything you would build as a component, prefer
renderVueComponent, which mounts it as a lazy-hydrated Vue island: the Vue runtime is shared across the page instead of being imported again by every inline block.
Example: Interactive Greeting¶
Below is a simple example where PHP generates static content, and Vue.js adds a dynamic, interactive element to the page.
Code¶
<?php
/** @var MageObsidian\ModernFrontend\Block\Template $block */
// PHP-rendered name
$userName = 'John Doe';
?>
<div class="greeting-container">
<!-- Static content rendered by PHP -->
<h1>Welcome, <?= $userName ?>!</h1>
<p>Update your greeting dynamically:</p>
<!-- Vue-powered dynamic section -->
<div id="vue-greeting-app">
<input type="text" v-model="greeting" placeholder="Enter your greeting" />
<p>Your dynamic greeting: <strong>{{ greeting }}</strong></p>
</div>
</div>
<script type="module">
import { createApp, ref } from '<?= $block->getViewLibFileUrl('vue') ?>';
// Initialize Vue app
createApp({
setup() {
// Reactive greeting state
const greeting = ref('Hello, <?= $userName ?>!');
return {
greeting
};
}
}).mount('#vue-greeting-app');
</script>
Explanation of the Example¶
-
PHP-Rendered Static Content
The initial greeting and page structure are rendered server-side by PHP:<h1>Welcome, <?= $userName ?>!</h1> -
Dynamic Interaction Managed by Vue.js
Vue.js handles the interactivity within the#vue-greeting-appcontainer: - An input field allows the user to type a custom greeting.
-
The greeting is displayed dynamically in real-time using Vue’s
v-model. -
Minimal Logic
The Vue logic is simple and localized to a small section, ensuring clarity and maintainability:const greeting = ref('Hello, <?= $userName ?>!');
Benefits of This Approach¶
- Simple and Effective: Combines PHP for static content with Vue for interactive features.
- Localized Reactivity: Vue is only applied to a specific section of the page, minimizing complexity.
- Quick Prototyping: Ideal for small, interactive elements without requiring separate Vue components.
Result¶
-
Initial Render:
Welcome, John Doe! Update your greeting dynamically: -
Dynamic Behavior:
- The user types in the input field, and the greeting updates instantly:
Your dynamic greeting: Hello, [User Input]!
Use Cases¶
- Quick enhancements to server-rendered pages.
- Adding interactivity to small sections without creating separate Vue components.
- Prototyping dynamic features with minimal overhead.