{"id":18287,"date":"2020-12-30T17:00:58","date_gmt":"2020-12-30T16:00:58","guid":{"rendered":"https:\/\/qodeinteractive.com\/magazine\/?p=18287"},"modified":"2020-12-28T14:54:30","modified_gmt":"2020-12-28T13:54:30","slug":"create-wordpress-custom-widget","status":"publish","type":"post","link":"https:\/\/qodeinteractive.com\/magazine\/create-wordpress-custom-widget\/","title":{"rendered":"How to Create a Custom WordPress Widget"},"content":{"rendered":"<div class=\"wpb-content-wrapper\"><p>[vc_row][vc_column][vc_column_text]WordPress is the most popular CMS (Content Management System) in large part due to its flexibility. Thanks to its plugins and widgets, any user can create and manage a website. Furthermore, plugin and theme authors are constantly enhancing the default WordPress functionalities, often by adding new widgets. In doing so, they are helping all users, especially those with little to no coding experience. But, widget creation isn\u2019t reserved only for the most advanced WordPress users. While it is true that creating new widgets is more suited to developers, there is no reason why any intermediate WordPress user shouldn\u2019t try to create a WordPress custom widget. By creating custom widgets for your website, you can add or extend any existing website features. In this article, we will tackle how this can be done and share an example that we made for this occasion.<br \/>\n[\/vc_column_text][vc_empty_space height=&#8221;68px&#8221;][\/vc_column][\/vc_row][vc_row][vc_column][vc_column_text]<\/p>\n<h2 class=\"qodef-h4\">What are WordPress widgets<\/h2>\n<p>[\/vc_column_text][vc_column_text]Before diving into the how-to part of this article, let us first clarify what WordPress widgets are. WordPress widgets are <strong>small pieces of content that are added to specialized areas called widget areas<\/strong>. Widget areas are located outside of the main page or post content.[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]Widgets display various kinds of information that, and depending on the widget area where they are put, they could show on all pages or just some. The placement and number of possible widget areas depend on the WordPress theme you are using. Generally, <strong>most themes have widget areas in the sidebar, footer, and header<\/strong>. Certain widgets are available by default, while others are made available through a theme or a plugin.[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]<strong>To find out which widgets and widget areas are at your disposal, you only need to navigate to Appearance &gt; Widgets.<\/strong> Then, if you want to use a certain widget, you can simply drag it from the list of <em>Available Widgets<\/em> on the left and drop it in the desired widget area on the right.[\/vc_column_text][vc_empty_space height=&#8221;50px&#8221;]<div class=\"qodef-single-image-holder   qodef-has-border \">\n    <div class=\"qodef-si-inner\" >\n                                    <img loading=\"lazy\" decoding=\"async\" width=\"996\" height=\"518\" src=\"https:\/\/qodeinteractive.com\/magazine\/wp-content\/uploads\/2020\/12\/Available-Widgets.jpg\" class=\"attachment-full size-full\" alt=\"Available Widgets\" srcset=\"https:\/\/qodeinteractive.com\/magazine\/wp-content\/uploads\/2020\/12\/Available-Widgets.jpg 996w, https:\/\/qodeinteractive.com\/magazine\/wp-content\/uploads\/2020\/12\/Available-Widgets-300x156.jpg 300w, https:\/\/qodeinteractive.com\/magazine\/wp-content\/uploads\/2020\/12\/Available-Widgets-768x399.jpg 768w, https:\/\/qodeinteractive.com\/magazine\/wp-content\/uploads\/2020\/12\/Available-Widgets-970x504.jpg 970w, https:\/\/qodeinteractive.com\/magazine\/wp-content\/uploads\/2020\/12\/Available-Widgets-620x322.jpg 620w\" sizes=\"auto, (max-width: 996px) 100vw, 996px\" \/>                        <\/div>\n<\/div>[vc_empty_space height=&#8221;38px&#8221;][vc_column_text]Sometimes, specific widgets would be most effective in areas of your website that aren\u2019t covered by a theme\u2019s widget areas. In those cases, a user may decide to <a href=\"https:\/\/qodeinteractive.com\/magazine\/add-widget-area-to-wordpress\/\">create custom widget areas<\/a> to display those widgets. However, there are cases where the desired functionality simply isn\u2019t available in the form of currently available widgets. Then, if you weren\u2019t able to find a suitable plugin, you should create a WordPress custom widget that meets your needs.[\/vc_column_text][vc_empty_space height=&#8221;80px&#8221;][vc_widget_sidebar sidebar_id=&#8221;new-top-picks-banner&#8221;][vc_empty_space height=&#8221;81px&#8221;][\/vc_column][\/vc_row][vc_row][vc_column][vc_column_text]<\/p>\n<h2 class=\"qodef-h4\">How to create a custom WordPress widget<\/h2>\n<p>[\/vc_column_text][vc_column_text]Creating a custom WordPress widget involves adding a specifically created code either into the <strong>functions.php file<\/strong> of your theme or into your <a href=\"https:\/\/qodeinteractive.com\/magazine\/wordpress-site-specific-plugin\/\">site-specific plugin<\/a>. Inserting the code should be done <a href=\"https:\/\/qodeinteractive.com\/magazine\/how-to-use-ftp\/\">via FTP<\/a>. We trust that you are familiar with the use of FTP, or have taken a look at our article on it and are ready to proceed. Without going into the details of inserting it, we can focus on the process of creating the code for a WordPress custom widget. It\u2019s important to stress that you should <a href=\"https:\/\/qodeinteractive.com\/magazine\/how-to-manually-backup-wordpress-website\/\">make a backup of your website<\/a> beforehand as any code error could break your website. After doing that, you can proceed to the steps required to create the code.[\/vc_column_text][vc_empty_space height=&#8221;72px&#8221;][\/vc_column][\/vc_row][vc_row][vc_column][vc_column_text]<\/p>\n<h3 class=\"qodef-h5\">Basic widget structure<\/h3>\n<p>[\/vc_column_text][vc_column_text]Since the 2.8.0 update, WordPress comes with a built-in class called <a href=\"https:\/\/developer.wordpress.org\/reference\/classes\/wp_widget\/\" target=\"_blank\" rel=\"noopener\">WP_Widget<\/a>, which is responsible for creating widgets. And every WordPress widget is made by extending the functionality of the WP_Widget class. The WP_Widget class currently has 18 available (non-deprecated) methods that can be used when creating new widgets.[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]For this article, we will cover the four most important methods, which are sufficient for creating a basic custom widget. Those are the following: <strong>__construct(), widget(), form()<\/strong> and <strong>update()<\/strong> methods. <strong>Depending on what kind of widget you wish to create, more methods might be needed.<\/strong> Those can be either some of the 18 belonging to the WP_Widget class or a specifically created method.[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]To create a basic custom widget, you need to insert the four methods mentioned above within your class to extend the WP_Widget class. And, to be able to use the Widget within the dashboard, you also need to register it using the <a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/register_widget\/\" target=\"_blank\" rel=\"noopener\">register_widget()<\/a> function. As such, the structure of a basic custom widget would look like this:[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">class Widget_Name extends WP_Widget {\r\nfunction __construct() {\r\n\/\/ Some code here\r\n}\r\npublic function widget( $args, $instance ) {\r\n\/\/ Some code here\r\n}\r\npublic function form( $instance ) {\r\n\/\/ Some code here\r\n}\r\npublic function update( $new_instance, $old_instance ) {\r\n\/\/ Some code here\r\n}\r\n}function name_of_the_register_function() {\r\nregister_widget( 'Widget_Name' );\r\n}add_action( 'widgets_init', 'name_of_the_register_function' );<\/pre>\n<p>[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]We\u2019re going to quickly elaborate the purpose of every piece of the code, before proceeding to study the example we made for this article.<br \/>\n[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]The <a href=\"https:\/\/developer.wordpress.org\/reference\/classes\/wp_widget\/__construct\/\" target=\"_blank\" rel=\"noopener\">__construct()<\/a> method creates the widget using four possible parameters: ID, name, widget, and control options. All parameters are optional, but the ID needs to be lowercase and unique.[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]The <a href=\"https:\/\/developer.wordpress.org\/reference\/classes\/wp_widget\/widget\/\" target=\"_blank\" rel=\"noopener\">widget()<\/a> method is the one that displays the widget content on the frontend.[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]The <a href=\"https:\/\/developer.wordpress.org\/reference\/classes\/wp_widget\/form\/\" target=\"_blank\" rel=\"noopener\">form()<\/a> method displays the widget options form in the backend. It is responsible both for the availability and the design of the options within the widget. These options are seen when the widget is added to a widget area.[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]The <a href=\"https:\/\/developer.wordpress.org\/reference\/classes\/wp_widget\/update\/\" target=\"_blank\" rel=\"noopener\">update()<\/a> method updates the options selected within the widget.[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]The content of each of the above-mentioned methods differs significantly based on the purpose of the widget. Technically speaking, it means that <strong>every subclass of the WP_Widget class (i.e. every widget) overrides the above-mentioned parent methods of the WP_Widget class differently.<\/strong>[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]Apart from that, you also need to register the widget. To do so, you need to \u201chook\u201d it to the corresponding <a href=\"https:\/\/developer.wordpress.org\/plugins\/hooks\/\" target=\"_blank\" rel=\"noopener\">hook<\/a> using the <a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/add_action\/\" target=\"_blank\" rel=\"noopener\">add_action()<\/a> function. You can do it by adding two arguments \u2013 \u201cwidgets_init\u201d as the hook and the name of your callback function. That callback function needs to contain the use of the register_widget function, with the widget class name as the argument (in our example that\u2019s <em>Widget_Name<\/em>).[\/vc_column_text][vc_empty_space height=&#8221;72px&#8221;][\/vc_column][\/vc_row][vc_row][vc_column][vc_column_text]<\/p>\n<h3 class=\"qodef-h5\">Our example<\/h3>\n<p>[\/vc_column_text][vc_column_text]Now that the widget structure is clearer, we can proceed to an actual example. We made the following widget for the purposes of this article.<br \/>\n[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">\/**\r\n* Adds Custom_Widget widget.\r\n*\/\r\nclass Custom_Widget extends WP_Widget {\r\n\/**\r\n* Register widget with WordPress.\r\n*\/\r\nfunction __construct() {\r\nparent::__construct(\r\n'custom_widget', \/\/ Base ID\r\nesc_html__( 'My Custom Widget', 'text_domain' ), \/\/ Name\r\narray( 'description' =&gt; esc_html__( 'A basic custom widget', 'text_domain' ), ) \/\/ Args\r\n);\r\n}\r\n\/**\r\n* Front-end display of the widget.\r\n*\r\n* @param array $args Widget arguments.\r\n* @param array $instance Saved values from the database.\r\n*\r\n* @see WP_Widget::widget()\r\n*\r\n*\/\r\npublic function widget( $args, $instance ) {\r\necho $args['before_widget'];\r\nif ( ! empty( $instance['title'] ) ) {\r\necho $args['before_title'] . apply_filters( 'widget_title', $instance['title'] ) . $args['after_title'];\r\n}\r\nif ( ! empty( $instance['text'] ) ) { ?&gt;\r\n&lt;p class=\"custom-widget-text\"&gt;\r\n&lt;?php echo nl2br( esc_html( $instance['text'] ) ) ?&gt;\r\n&lt;\/p&gt;\r\n&lt;?php }\r\nif ( ! empty( $instance['link'] ) ) { ?&gt;\r\n&lt;a href=\"&lt;?php echo esc_url( $instance['link'] ); ?&gt;\"\r\ntarget=\"&lt;?php echo esc_attr( $instance['link_target'] ); ?&gt;\" class=\"custom-widget-link\"&gt;\r\n&lt;span&gt;&lt;?php echo esc_html__( 'Learn more', 'text_domain' ); ?&gt;&lt;\/span&gt;\r\n&lt;\/a&gt;\r\n&lt;?php }\r\necho $args['after_widget'];\r\n}\r\n\/**\r\n* Back-end widget form.\r\n*\r\n* @param array $instance Previously saved values from the database.\r\n*\r\n* @see WP_Widget::form()\r\n*\r\n*\/\r\npublic function form( $instance ) {\r\n$title = ! empty( $instance['title'] ) ? $instance['title'] : '';\r\n$text = ! empty( $instance['text'] ) ? $instance['text'] : '';\r\n$link = ! empty( $instance['link'] ) ? $instance['link'] : '';\r\n$link_target = ! empty( $instance['link_target'] ) ? $instance['link_target'] : '_blank';\r\n?&gt;\r\n&lt;!-- Title --&gt;\r\n&lt;p&gt;\r\n&lt;label for=\"&lt;?php echo esc_attr( $this-&gt;get_field_id( 'title' ) ); ?&gt;\"&gt;\r\n&lt;?php esc_attr__( 'Widget Title:', 'text_domain' ); ?&gt;\r\n&lt;\/label&gt;\r\n&lt;input class=\"widefat\" id=\"&lt;?php echo esc_attr( $this-&gt;get_field_id( 'title' ) ); ?&gt;\"\r\nname=\"&lt;?php echo esc_attr( $this-&gt;get_field_name( 'title' ) ); ?&gt;\" type=\"text\"\r\nvalue=\"&lt;?php echo esc_attr( $title ); ?&gt;\"&gt;\r\n&lt;\/p&gt;\r\n&lt;!-- Text --&gt;\r\n&lt;p&gt;\r\n&lt;label for=\"&lt;?php echo esc_attr( $this-&gt;get_field_id( 'text' ) ); ?&gt;\"&gt;\r\n&lt;?php esc_attr__( 'Widget Text:', 'text_domain' ); ?&gt;\r\n&lt;\/label&gt;\r\n&lt;textarea class=\"widefat\" rows=\"16\" cols=\"20\" id=\"&lt;?php echo esc_attr( $this-&gt;get_field_id( 'text' ) ); ?&gt;\"\r\nname=\"&lt;?php echo esc_attr( $this-&gt;get_field_name( 'text' ) ); ?&gt;\"&gt;&lt;?php echo esc_attr( $text ); ?&gt;\r\n&lt;\/textarea&gt;\r\n&lt;\/p&gt;\r\n&lt;!-- Link URL --&gt;\r\n&lt;p&gt;\r\n&lt;label for=\"&lt;?php echo esc_attr( $this-&gt;get_field_id( 'link' ) ); ?&gt;\"&gt;\r\n&lt;?php esc_attr__( 'Insert URL:', 'text_domain' ); ?&gt;\r\n&lt;\/label&gt;\r\n&lt;input class=\"widefat\" id=\"&lt;?php echo esc_attr( $this-&gt;get_field_id( 'link' ) ); ?&gt;\"\r\nname=\"&lt;?php echo esc_attr( $this-&gt;get_field_name( 'link' ) ); ?&gt;\" type=\"text\"\r\nvalue=\"&lt;?php echo esc_attr( $link ); ?&gt;\"&gt;\r\n&lt;\/p&gt;\r\n&lt;!-- Link target --&gt;\r\n&lt;p&gt;\r\n&lt;label for=\"&lt;?php echo esc_attr( $this-&gt;get_field_id( 'link_target' ) ); ?&gt;\"&gt;\r\n&lt;?php esc_attr_e( 'Open link in a new tab?', 'text_domain' ); ?&gt;\r\n&lt;\/label&gt;\r\n&lt;select class=\"widefat\" id=\"&lt;?php echo esc_attr( $this-&gt;get_field_id( 'link_target' ) ); ?&gt;\"\r\nname=\"&lt;?php echo esc_attr( $this-&gt;get_field_name( 'link_target' ) ); ?&gt;\"&gt;\r\n&lt;option value=\"_blank\" &lt;?php echo ( $link_target == '_blank' ) ? 'selected' : ''; ?&gt;&gt;Yes&lt;\/option&gt;\r\n&lt;option value=\"_self\" &lt;?php echo ( $link_target == '_self' ) ? 'selected' : ''; ?&gt;&gt;No&lt;\/option&gt;\r\n&lt;\/select&gt;\r\n&lt;\/p&gt;\r\n&lt;?php\r\n}\r\n\/**\r\n* Sanitize widget form values as they are saved.\r\n*\r\n* @param array $new_instance Values just sent to be saved.\r\n* @param array $old_instance Previously saved values from the database.\r\n*\r\n* @return array Updated safe values to be saved.\r\n* @see WP_Widget::update()\r\n*\r\n*\/\r\npublic function update( $new_instance, $old_instance ) {\r\n$instance = array();\r\n$instance['title'] = ( ! empty( $new_instance['title'] ) ) ? sanitize_text_field( $new_instance['title'] ) : '';\r\nif ( current_user_can( 'unfiltered_html' ) ) {\r\n$instance['text'] = ( ! empty( $new_instance['text'] ) ) ? $new_instance['text'] : '';\r\n} else {\r\n$instance['text'] = ( ! empty( $new_instance['text'] ) ) ? sanitize_text_field( $new_instance['text'] ) : '';\r\n}\r\n$instance['link'] = ( ! empty( $new_instance['link'] ) ) ? sanitize_text_field( $new_instance['link'] ) : '';\r\n$instance['link_target'] = ( ! empty( $new_instance['link_target'] ) ) ? sanitize_text_field( $new_instance['link_target'] ) : '';\r\nreturn $instance;\r\n}\r\n} \/\/ class Custom_Widget\r\n\/\/ register Custom_Widget widget\r\nfunction register_custom_widget() {\r\nregister_widget( 'Custom_Widget' );\r\n}\r\nadd_action( 'widgets_init', 'register_custom_widget' );<\/pre>\n<p>[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]Even though this code may appear complicated at first glance, the widget it creates is rather simple. We developed it from the basic widget example given on the <a href=\"https:\/\/codex.wordpress.org\/Widgets_API\" target=\"_blank\" rel=\"noopener\">Widgets API<\/a> page, which we suggest you peruse. <strong>The code is properly commented to make it easier to review.<\/strong> Let us do so, part by part.[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]Firstly, the widget is created using a subclass called <strong>Custom_Widget<\/strong> that extends the <strong>WP_Widget<\/strong> class. The same widget is registered at the end, by using the <strong>register_widget()<\/strong> function and the <strong>widgets_init<\/strong> hook.[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">\/**\r\n* Adds Custom_Widget widget.\r\n*\/\r\nclass Custom_Widget extends WP_Widget {\r\n\/\/ Some code here\r\n} \/\/ class Custom_Widget\r\n\/\/ register Custom_Widget widget\r\nfunction register_custom_widget() {\r\nregister_widget( 'Custom_Widget' );\r\n}\r\nadd_action( 'widgets_init', 'register_custom_widget' );<\/pre>\n<p>[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]The Custom_Widget class has four methods. Using the <strong>__construct()<\/strong> method, we created a widget with <strong>custom_widget<\/strong> as its ID. The other two arguments mean that the name of this widget is <strong>My Custom Widget<\/strong>, while the description is <strong>A basic custom widget<\/strong>. You will be able to see this after navigating to Appearance &gt; Widgets, within the <em>Available Widgets<\/em> section.[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">\/**\r\n* Register widget with WordPress.\r\n*\/\r\nfunction __construct() {\r\nparent::__construct(\r\n'custom_widget', \/\/ Base ID\r\nesc_html__( 'My Custom Widget', 'text_domain' ), \/\/ Name\r\narray( 'description' =&gt; esc_html__( 'A basic custom widget', 'text_domain' ), ) \/\/ Args\r\n);\r\n}<\/pre>\n<p>[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]The <strong>widget()<\/strong> method displays three parts of the widget\u2014<strong>the title of the widget, a piece of text, and a <em>Learn more<\/em> link.<\/strong> By wrapping each of the parts with <a href=\"https:\/\/www.php.net\/manual\/en\/control-structures.if.php\" target=\"_blank\" rel=\"noopener\">if{\u2026} statements<\/a>, we have made sure that those parts are only shown if they are inserted within the widget options. We also included the \u2018before_widget\u2019 and \u2018after_widget\u2019 values around the widget content, and the \u2018before_title\u2019 and \u2018after_title\u2019 values around the widget title. These four values are defined for all widget areas, default or custom. That is done during the widget area registration process using the <a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/register_sidebar\/\" target=\"_blank\" rel=\"noopener\">register_sidebar()<\/a> function.[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]Additionally, the labels were <a href=\"https:\/\/developer.wordpress.org\/themes\/theme-security\/data-sanitization-escaping\/\" target=\"_blank\" rel=\"noopener\">escaped for secure use<\/a> with the following functions: <a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/esc_attr\/\" target=\"_blank\" rel=\"noopener\">esc_attr()<\/a>, <a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/esc_url\/\" target=\"_blank\" rel=\"noopener\">esc_url()<\/a>, <a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/esc_html\/\" target=\"_blank\" rel=\"noopener\">esc_html()<\/a>, and <a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/esc_html__\/\" target=\"_blank\" rel=\"noopener\">esc_html__()<\/a>. Furthermore, most of the labels are translatable. However, to translate them, you would need to make sure that the <strong>text_domain<\/strong> string, which was added as a placeholder translate domain, is replaced with a proper theme or site-specific plugin translate domain. This is especially helpful for the <em>Learn more<\/em> label, placed on the link.[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]Finally, by further wrapping the text with the <a href=\"https:\/\/www.php.net\/manual\/en\/function.nl2br.php\" target=\"_blank\" rel=\"noopener\">nl2br()<\/a> function, we have ensured that any line breaks that might have been inserted in the textarea in the backend are properly displayed in the frontend.[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">\/**\r\n* Front-end display of the widget.\r\n*\r\n* @param array $args Widget arguments.\r\n* @param array $instance Saved values from the database.\r\n*\r\n* @see WP_Widget::widget()\r\n*\r\n*\/\r\npublic function widget( $args, $instance ) {\r\necho $args['before_widget'];\r\nif ( ! empty( $instance['title'] ) ) {\r\necho $args['before_title'] . apply_filters( 'widget_title', $instance['title'] ) . $args['after_title'];\r\n}\r\nif ( ! empty( $instance['text'] ) ) { ?&gt;\r\n&lt;p class=\"custom-widget-text\"&gt;\r\n&lt;?php echo nl2br( esc_html( $instance['text'] ) ) ?&gt;\r\n&lt;\/p&gt;\r\n&lt;?php }\r\nif ( ! empty( $instance['link'] ) ) { ?&gt;\r\n&lt;a href=\"&lt;?php echo esc_url( $instance['link'] ); ?&gt;\"\r\ntarget=\"&lt;?php echo esc_attr( $instance['link_target'] ); ?&gt;\" class=\"custom-widget-link\"&gt;\r\n&lt;span&gt;&lt;?php echo esc_html__( 'Learn more', 'text_domain' ); ?&gt;&lt;\/span&gt;\r\n&lt;\/a&gt;\r\n&lt;?php }\r\necho $args['after_widget'];<\/pre>\n<p>[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]Using the <strong>form()<\/strong> function, we have created four simple widget option fields. Those are the following:[\/vc_column_text][vc_empty_space height=&#8221;22px&#8221;]<ul class=\"qodef-unordered-list-item qodef-toc\">\n    <li>\n\t        <div class=\"qodef-ul-title-holder\">\n            <span class=\"qodef-ul-title-content\"><strong>an input field for inserting a widget title<\/strong>, which has the description Widget Title: above it,<\/span>        <\/div>\n            <\/li>\n<\/ul><ul class=\"qodef-unordered-list-item qodef-toc\">\n    <li>\n\t        <div class=\"qodef-ul-title-holder\">\n            <span class=\"qodef-ul-title-content\"><strong>a textarea for inserting text<\/strong>, with the description Widget Text: above,<\/span>        <\/div>\n            <\/li>\n<\/ul><ul class=\"qodef-unordered-list-item qodef-toc\">\n    <li>\n\t        <div class=\"qodef-ul-title-holder\">\n            <span class=\"qodef-ul-title-content\"><strong>an input field for inserting the URL<\/strong>, with the description Insert URL: above, and<\/span>        <\/div>\n            <\/li>\n<\/ul><ul class=\"qodef-unordered-list-item qodef-toc\">\n    <li>\n\t        <div class=\"qodef-ul-title-holder\">\n            <span class=\"qodef-ul-title-content\"><strong>a select field for choosing if the link is to open a new tab or not<\/strong>, with two options\u2014Yes and No, and a description above asking Open link in a new tab?<\/span>        <\/div>\n            <\/li>\n<\/ul>[vc_empty_space height=&#8221;28px&#8221;][vc_column_text]None of the fields except the last one have any default values. And, for the last one, the <em>Yes<\/em> value is set as a default within the select field, meaning the link target is set to <em>_blank<\/em> by default.[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">\/**\r\n* Back-end widget form.\r\n*\r\n* @param array $instance Previously saved values from the database.\r\n*\r\n* @see WP_Widget::form()\r\n*\r\n*\/\r\npublic function form( $instance ) {\r\n$title = ! empty( $instance['title'] ) ? $instance['title'] : '';\r\n$text = ! empty( $instance['text'] ) ? $instance['text'] : '';\r\n$link = ! empty( $instance['link'] ) ? $instance['link'] : '';\r\n$link_target = ! empty( $instance['link_target'] ) ? $instance['link_target'] : '_blank';\r\n?&gt;\r\n&lt;!-- Title --&gt;\r\n&lt;p&gt;\r\n&lt;label for=\"&lt;?php echo esc_attr( $this-&gt;get_field_id( 'title' ) ); ?&gt;\"&gt;\r\n&lt;?php esc_attr__( 'Widget Title:', 'text_domain' ); ?&gt;\r\n&lt;\/label&gt;\r\n&lt;input class=\"widefat\" id=\"&lt;?php echo esc_attr( $this-&gt;get_field_id( 'title' ) ); ?&gt;\"\r\nname=\"&lt;?php echo esc_attr( $this-&gt;get_field_name( 'title' ) ); ?&gt;\" type=\"text\"\r\nvalue=\"&lt;?php echo esc_attr( $title ); ?&gt;\"&gt;\r\n&lt;\/p&gt;\r\n&lt;!-- Text --&gt;\r\n&lt;p&gt;\r\n&lt;label for=\"&lt;?php echo esc_attr( $this-&gt;get_field_id( 'text' ) ); ?&gt;\"&gt;\r\n&lt;?php esc_attr__( 'Widget Text:', 'text_domain' ); ?&gt;\r\n&lt;\/label&gt;\r\n&lt;textarea class=\"widefat\" rows=\"16\" cols=\"20\" id=\"&lt;?php echo esc_attr( $this-&gt;get_field_id( 'text' ) ); ?&gt;\"\r\nname=\"&lt;?php echo esc_attr( $this-&gt;get_field_name( 'text' ) ); ?&gt;\"&gt;&lt;?php echo esc_attr( $text ); ?&gt;\r\n&lt;\/textarea&gt;\r\n&lt;\/p&gt;\r\n&lt;!-- Link URL --&gt;\r\n&lt;p&gt;\r\n&lt;label for=\"&lt;?php echo esc_attr( $this-&gt;get_field_id( 'link' ) ); ?&gt;\"&gt;\r\n&lt;?php esc_attr__( 'Insert URL:', 'text_domain' ); ?&gt;\r\n&lt;\/label&gt;\r\n&lt;input class=\"widefat\" id=\"&lt;?php echo esc_attr( $this-&gt;get_field_id( 'link' ) ); ?&gt;\"\r\nname=\"&lt;?php echo esc_attr( $this-&gt;get_field_name( 'link' ) ); ?&gt;\" type=\"text\"\r\nvalue=\"&lt;?php echo esc_attr( $link ); ?&gt;\"&gt;\r\n&lt;\/p&gt;\r\n&lt;!-- Link target --&gt;\r\n&lt;p&gt;\r\n&lt;label for=\"&lt;?php echo esc_attr( $this-&gt;get_field_id( 'link_target' ) ); ?&gt;\"&gt;\r\n&lt;?php esc_attr_e( 'Open link in a new tab?', 'text_domain' ); ?&gt;\r\n&lt;\/label&gt;\r\n&lt;select class=\"widefat\" id=\"&lt;?php echo esc_attr( $this-&gt;get_field_id( 'link_target' ) ); ?&gt;\"\r\nname=\"&lt;?php echo esc_attr( $this-&gt;get_field_name( 'link_target' ) ); ?&gt;\"&gt;\r\n&lt;option value=\"_blank\" &lt;?php echo ( $link_target == '_blank' ) ? 'selected' : ''; ?&gt;&gt;Yes&lt;\/option&gt;\r\n&lt;option value=\"_self\" &lt;?php echo ( $link_target == '_self' ) ? 'selected' : ''; ?&gt;&gt;No&lt;\/option&gt;\r\n&lt;\/select&gt;\r\n&lt;\/p&gt;\r\n&lt;?php\r\n}<\/pre>\n<p>[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]We used the <strong>update()<\/strong> method to save the values within the fields into the $instance associative array. But, the values are first sanitized using the <a href=\"https:\/\/developer.wordpress.org\/reference\/functions\/sanitize_text_field\/\" target=\"_blank\" rel=\"noopener\">sanitize_text_field() function<\/a>. This is true for all fields, except for the textarea, which requires a special explanation.[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]The <strong>sanitize_text_field() <\/strong>function strips the argument of any unnecessary white spaces, tabs, or line breaks. Therefore, to ensure that multi-paragraph texts are displayed properly, we have used the <strong>nl2br()<\/strong> function in the <strong>widget()<\/strong> method, as mentioned before.[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]Furthermore, with the use of <a href=\"http:\/\/phpweb.hostnet.com.br\/manual\/en\/control-structures.else.php\" target=\"_blank\" rel=\"noopener\">if{\u2026} else {\u2026} statement<\/a> next to the textarea part we have ensured that users with the privileges for inserting unfiltered HTML code are free to do so. On the other hand, if any user without such a privilege inserts HTML code into the textarea, all tags from that code will be stripped thanks to the sanitize_text_field() function. The users that have such privileges, or more precisely capabilities, are (in the case of a regular WordPress website) the ones with the <strong>administrator<\/strong> or <strong>editor<\/strong> <a href=\"https:\/\/qodeinteractive.com\/magazine\/wordpress-user-roles\/\">user roles<\/a>. In the case of a WordPress multisite, such privileges are only reserved for the <strong>super admin<\/strong> user role.[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">\/**\r\n* Sanitize widget form values as they are saved.\r\n*\r\n* @param array $new_instance Values just sent to be saved.\r\n* @param array $old_instance Previously saved values from the database.\r\n*\r\n* @return array Updated safe values to be saved.\r\n* @see WP_Widget::update()\r\n*\r\n*\/\r\npublic function update( $new_instance, $old_instance ) {\r\n$instance = array();\r\n$instance['title'] = ( ! empty( $new_instance['title'] ) ) ? sanitize_text_field( $new_instance['title'] ) : '';\r\nif ( current_user_can( 'unfiltered_html' ) ) {\r\n$instance['text'] = ( ! empty( $new_instance['text'] ) ) ? $new_instance['text'] : '';\r\n} else {\r\n$instance['text'] = ( ! empty( $new_instance['text'] ) ) ? sanitize_text_field( $new_instance['text'] ) : '';\r\n}\r\n$instance['link'] = ( ! empty( $new_instance['link'] ) ) ? sanitize_text_field( $new_instance['link'] ) : '';\r\n$instance['link_target'] = ( ! empty( $new_instance['link_target'] ) ) ? sanitize_text_field( $new_instance['link_target'] ) : '';\r\nreturn $instance;\r\n}<\/pre>\n<p>[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]Since we\u2019ve examined how the code was put together, let us explain how you can use this widget. Start by <strong>navigating to Appearance &gt; Widgets<\/strong> and <strong>looking for the widget name you used in the <em>__construct()<\/em> function<\/strong>. In our case, the widget is called <em>My Custom Widget<\/em>.<strong> Drag and drop it in the widget area of your choice and adjust its options according to your needs.<\/strong> As we explained before, you can choose a widget title, text, link below the text, and whether the link should open in a new tab or not. After filling out the options, <strong>press the <em>Save<\/em> button<\/strong> below. The output of the widget depends on your default theme stylization. In our case, using the <a href=\"https:\/\/qodeinteractive.com\/wordpress-theme\/lekker-portfolio-wordpress-theme\/\">Lekker WordPress theme<\/a>, your widget could look similar to the one below.[\/vc_column_text][vc_empty_space height=&#8221;50px&#8221;]<div class=\"qodef-single-image-holder   qodef-has-border \">\n    <div class=\"qodef-si-inner\" >\n                                    <img loading=\"lazy\" decoding=\"async\" width=\"969\" height=\"518\" src=\"https:\/\/qodeinteractive.com\/magazine\/wp-content\/uploads\/2020\/12\/My-Custom-Widget.jpg\" class=\"attachment-full size-full\" alt=\"My Custom Widget\" srcset=\"https:\/\/qodeinteractive.com\/magazine\/wp-content\/uploads\/2020\/12\/My-Custom-Widget.jpg 969w, https:\/\/qodeinteractive.com\/magazine\/wp-content\/uploads\/2020\/12\/My-Custom-Widget-300x160.jpg 300w, https:\/\/qodeinteractive.com\/magazine\/wp-content\/uploads\/2020\/12\/My-Custom-Widget-768x411.jpg 768w, https:\/\/qodeinteractive.com\/magazine\/wp-content\/uploads\/2020\/12\/My-Custom-Widget-620x331.jpg 620w\" sizes=\"auto, (max-width: 969px) 100vw, 969px\" \/>                        <\/div>\n<\/div>[vc_empty_space height=&#8221;38px&#8221;][vc_column_text]Depending on the functionality you wish to add, you might need to insert additional JS code for the widget to work properly. Alternatively, you might need to include some CSS code, to adjust the stylization. As this depends entirely on the type of widget you wish to make, creating such JS or CSS code should be done on a case-by-case basis.<br \/>\n[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]In our case, since our example WordPress custom widget is quite simple, we only needed a small amount of CSS to stylize the content. Namely, the link we included. For the widget developed for this article, we created the CSS shown below. As our widget is simple, it required only a small style adjustment. And this amount of CSS can safely be included in Appearance &gt; Customize &gt; Additional CSS.<br \/>\n[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">.custom-widget-link {\r\ndisplay: inline-block;\r\nbackground-color: #000;\r\nmargin-top: 10px;\r\nfont-weight: 600;\r\nborder-radius: 5px;\r\npadding: 14px 26px;\r\n}\r\n.custom-widget-link span {\r\ncolor: #fff;\r\n}<\/pre>\n<p>[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]However, we must note that <strong>the best practice for adding widget-related CSS and JS code is to enqueue it.<\/strong> For that, <strong>you need to create separate .css and .js files with the code, place the files within the folder of your theme (or within the folder of the site-specific plugin, depending on where you placed the initial code), and use the wp_enqueue_style and wp_enqueue_script functions.<\/strong> For more information on this topic, we advise you to take a look at our article on <a href=\"https:\/\/qodeinteractive.com\/magazine\/how-to-enque-scripts-wordpress\/\">enqueueing custom scripts and stylesheets in WordPress<\/a>.[\/vc_column_text][vc_empty_space height=&#8221;68px&#8221;][\/vc_column][\/vc_row][vc_row][vc_column][vc_column_text]<\/p>\n<h2 class=\"qodef-h4\">Final Thoughts<\/h2>\n<p>[\/vc_column_text][vc_column_text]Creating custom WordPress widgets might seem daunting at first since it requires a great deal of prior coding and WordPress knowledge. However, widgets can be quite straightforward to create. That\u2019s mostly because they are very well structured and the process for creating them has a lot of run-of-the-mill steps.<br \/>\n[\/vc_column_text][vc_empty_space height=&#8221;28px&#8221;][vc_column_text]We endeavored to explain each of the steps necessary, both in theory as well as in practice using a carefully created example. If you follow these steps closely, we are confident that you will manage to create a custom widget of your own. Hopefully, you will have learned something new in the process, as well!<br \/>\n[\/vc_column_text][\/vc_column][\/vc_row]<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>Do some next-level modification of your website by creating a WordPress custom widget. Our tutorial will take you step-by-step through the process.<\/p>\n","protected":false},"author":11229,"featured_media":18304,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[21,4,62,13],"class_list":["post-18287","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tutorials","tag-php","tag-tips","tag-widgets","tag-wordpress"],"_links":{"self":[{"href":"https:\/\/qodeinteractive.com\/magazine\/wp-json\/wp\/v2\/posts\/18287","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/qodeinteractive.com\/magazine\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/qodeinteractive.com\/magazine\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/qodeinteractive.com\/magazine\/wp-json\/wp\/v2\/users\/11229"}],"replies":[{"embeddable":true,"href":"https:\/\/qodeinteractive.com\/magazine\/wp-json\/wp\/v2\/comments?post=18287"}],"version-history":[{"count":0,"href":"https:\/\/qodeinteractive.com\/magazine\/wp-json\/wp\/v2\/posts\/18287\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/qodeinteractive.com\/magazine\/wp-json\/wp\/v2\/media\/18304"}],"wp:attachment":[{"href":"https:\/\/qodeinteractive.com\/magazine\/wp-json\/wp\/v2\/media?parent=18287"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/qodeinteractive.com\/magazine\/wp-json\/wp\/v2\/categories?post=18287"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/qodeinteractive.com\/magazine\/wp-json\/wp\/v2\/tags?post=18287"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}