Skip to content

Commit 60a41df

Browse files
Add "Widget Output Caching" (#278)
1 parent 4157483 commit 60a41df

File tree

5 files changed

+191
-0
lines changed

5 files changed

+191
-0
lines changed

src/.vuepress/sidebars/widgets.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ module.exports = [
4646
'rendering-repeaters',
4747
'rendering-html-attribute',
4848
'rendering-inline-editing',
49+
'widget-output-caching',
4950
]
5051
},
5152
]

src/widgets/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Learn more about widget anatomy and how to create your own:
3232
* [Rendering Repeater](./rendering-repeaters/)
3333
* [Rendering HTML Attribute](./rendering-html-attribute/)
3434
* [Rendering Inline Editing](./rendering-inline-editing/)
35+
* [Widget Output Caching](./widget-output-caching/)
3536

3637
## Code Examples
3738

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
## Widget Output Caching
2+
3+
<Badge type="tip" vertical="top" text="Elementor Core" /> <Badge type="warning" vertical="top" text="Intermediate" />
4+
5+
Elementor offers a feature to minimize the impact of widgets on page performance. For widgets that generate static output, Elementor can cache the HTML, avoiding the need to render it each time the page loads.
6+
7+
By default, Elementor does not cache widget outputs and renders all widgets on the page with every page load. However, developers can enable HTML output caching for widgets to enhance loading speeds.
8+
9+
## Widgets Rendering Mechanism
10+
11+
During page load, Elementor renders all widgets to generate the required markup. The more widgets the page has, the slower the rendering process.
12+
13+
To optimize this, Elementor's widget caching mechanism can be employed. This mechanism renders a widget once, caches the output, and uses the cached version for subsequent page loads. This reduces server memory usage by 99% and improves Time To First Byte (TTFB) on the frontend.
14+
15+
### Content Types
16+
17+
Widgets can return two types of output:
18+
19+
* **Static Content** - return the same content each time, for every user.
20+
* **Dynamic Content** - return content that may change based on different parameters.
21+
22+
A static content example is a heading widget that consistently returns the text from a control. Since the output is identical for all users, it can be cached.
23+
24+
In contrast, dynamic content involves logic that alters the output based on different conditions. For example, a widget that includes a PHP function to display the user's name will show different results for different users, thus the widget should not cache the output.
25+
26+
An extreme case of dynamic content is a widget that displays a random number on each page load. Such a widget should not cache its output.
27+
28+
The widget caching mechanism in Elementor is sophisticated and includes several exceptions to avoid inappropriate caching. For instance, if one of the widget controls uses a dynamic tag or has a display condition, Elementor will bypass the cache, fully rendering the widget.
29+
30+
### Caching Output
31+
32+
To enhance widget performance by caching static content, apply the following method to the widget class:
33+
34+
```php
35+
class Elementor_Test_Widget extends \Elementor\Widget_Base {
36+
37+
protected function is_dynamic_content() {
38+
return false;
39+
}
40+
41+
}
42+
```
43+
44+
Using the code above, the widget instructs Elementor to cache its output if it generates static content.
45+
46+
By default, all widgets are treated as dynamic and are not cached. If you are not sure, avoid using this method.
47+
48+
### Exceptions
49+
50+
The widget caching mechanism in Elementor is sophisticated and includes several exceptions to avoid inappropriate caching. For instance, if one of the widget controls uses a dynamic tag or has a display condition, Elementor will bypass the cache, fully rendering the widget.
51+
52+
## Examples
53+
54+
### A Widget With a Static Output
55+
56+
Let's say you have a widget with a single control in which the users can set a "title". The render function will always render the same HTML for all the users, therefore the output can be cached:
57+
58+
```php {4-6,38-40,51-53}
59+
<?php
60+
class Elementor_Test_Widget extends \Elementor\Widget_Base {
61+
62+
protected function is_dynamic_content() {
63+
return false;
64+
}
65+
66+
protected function register_controls() {
67+
68+
$this->start_controls_section(
69+
'section_content',
70+
[
71+
'label' => esc_html__( 'Content', 'textdomain' ),
72+
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
73+
]
74+
);
75+
76+
$this->add_control(
77+
'title',
78+
[
79+
'label' => esc_html__( 'Title', 'textdomain' ),
80+
'type' => \Elementor\Controls_Manager::TEXT,
81+
'placeholder' => esc_html__( 'Enter your title', 'textdomain' ),
82+
]
83+
);
84+
85+
$this->end_controls_section();
86+
87+
}
88+
89+
protected function render() {
90+
$settings = $this->get_settings_for_display();
91+
92+
if ( empty( $settings['title'] ) ) {
93+
return;
94+
}
95+
?>
96+
<h3>
97+
<?php echo $settings['title']; ?>
98+
</h3>
99+
<?php
100+
}
101+
102+
protected function content_template() {
103+
?>
104+
<#
105+
if ( '' === settings.title ) {
106+
return;
107+
}
108+
#>
109+
<h3>
110+
{{{ settings.title }}}
111+
</h3>
112+
<?php
113+
}
114+
115+
}
116+
```
117+
118+
### A Widget With a Dynamic Output
119+
120+
Let's say you have the same widget, but the render function has custom code that may produce different HTML far different users, therefore the output can't be cached:
121+
122+
```php {34-36,38-40,51-53,55-57}
123+
<?php
124+
class Elementor_Test_Widget extends \Elementor\Widget_Base {
125+
126+
protected function register_controls() {
127+
128+
$this->start_controls_section(
129+
'section_content',
130+
[
131+
'label' => esc_html__( 'Content', 'textdomain' ),
132+
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
133+
]
134+
);
135+
136+
$this->add_control(
137+
'title',
138+
[
139+
'label' => esc_html__( 'Title', 'textdomain' ),
140+
'type' => \Elementor\Controls_Manager::TEXT,
141+
'placeholder' => esc_html__( 'Enter your title', 'textdomain' ),
142+
]
143+
);
144+
145+
$this->end_controls_section();
146+
147+
}
148+
149+
protected function render() {
150+
$settings = $this->get_settings_for_display();
151+
152+
if ( empty( $settings['title'] ) ) {
153+
return;
154+
}
155+
156+
if ( is_user_logged_in() ) {
157+
$greeting = esc_html__( 'Hi logged in user!', 'textdomain' ) . ' ';
158+
}
159+
?>
160+
<h3>
161+
<?php echo $greeting . $settings['title']; ?>
162+
</h3>
163+
<?php
164+
}
165+
166+
protected function content_template() {
167+
?>
168+
<#
169+
if ( '' === settings.title ) {
170+
return;
171+
}
172+
173+
if ( document.body.classList.contains( 'logged-in' ) ) {
174+
greeting = 'Hi logged in user! ';
175+
}
176+
#>
177+
<h3>
178+
{{{ greeting + settings.title }}}
179+
</h3>
180+
<?php
181+
}
182+
183+
}
184+
```
185+
186+
This widget generates different HTML output for logged-in users and anonymous users. We can't cache the widget output as the cached HTML may be displayed for the wrong users.

src/widgets/widget-rendering.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ We're going to dive in and see how this is done using the different methods. We'
3535
* [Rendering Repeater](./rendering-repeaters/)
3636
* [Rendering HTML Attribute](./rendering-html-attribute/)
3737
* [Rendering Inline Editing](./rendering-inline-editing/)
38+
* [Widget Output Caching](./widget-output-caching/)

src/widgets/widget-structure.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ class Elementor_Test_Widget extends \Elementor\Widget_Base {
4040

4141
public function get_style_depends() {}
4242

43+
protected function is_dynamic_content() {}
44+
4345
protected function register_controls() {}
4446

4547
protected function render() {}

0 commit comments

Comments
 (0)