In this first part of our Extending Dokan series, I’ll be showing you how to add a custom settings tab to the Dokan vendor dashboard. To illustrate the basic concepts involved, I will be creating an “About” tab where vendors can enter some personal information about themselves. The end result will look like this:

This is how our About tab should look when we’re done

I’ll admit that this isn’t the most practical example, but it should be enough to show you the basic workings of the Dokan settings API. That being said, let’s write some code!

Note: The code examples in this article use the generic vendor prefix “prefix_” for function and meta key names. In addition, text domains are omitted wherever internationalization functions are used. You should replace the generic vendor prefix with your own prefix and insert your text domain where appropriate when incorporating these code snippets into your plugin or theme.

Step 1: Adding our tab to the Dokan settings menu

The first thing we need to do is add a menu item for our settings page. To do so, we’ll use the dokan_get_dashboard_settings_nav filter.

Here’s what the code looks like:

 * Adds an 'About' tab to the Dokan settings navigation menu.
 * @param array $menu_items
 * @return array
function prefix_add_about_tab( $menu_items ) {
    $menu_items['about'] = [
        'title'      => __( 'About' ),
        'icon'       => '<i class="fa fa-user-circle"></i>',
        'url'        => dokan_get_navigation_url( 'settings/about' ),
        'pos'        => 90,
        'permission' => 'dokan_view_store_settings_menu',

    return $menu_items;
add_filter( 'dokan_get_dashboard_settings_nav', 'prefix_add_about_tab' );

As you can see, we get as input an associative array of menu items and return a modified array with our menu item inserted. Every menu item is itself an associative array with the following array keys:

Key (* denotes required) Description
title* The label for the menu item, in our case “About”.
icon* The icon to use for the menu item. You can insert your own icon or use one of the icons from FontAwesome 4.7.0.
url* The URL to open when the menu item is clicked.
pos This controls the order of the menu item in the settings menu. In this case I’ve set pos such that the menu item appears between Shipping and Social Profiles. You can omit this value, in which case the default value of 200 is used.
permission The capability required to view the menu item. If omitted, all users can view the menu item.

After this step, our settings page looks like this:

The settings page after step 1 is completed

Step 2: Setting a custom title for our tab

We’re getting a little closer to our target, but we still have a ways to go. Let’s start by changing the title of our settings page to “About Me” and adding some help text to describe the page. To accomplish this, we’ll use the dokan_dashboard_settings_heading_title and dokan_dashboard_settings_helper_text filters, respectively.

Let’s take a look at the code:

 * Sets the title for the 'About' settings tab.
 * @param string $title
 * @param string $tab
 * @return string Title for tab with slug $tab
function prefix_set_about_tab_title( $title, $tab ) {
    if ( 'about' === $tab ) {
        $title = __( 'About Me' );

    return $title;

add_filter( 'dokan_dashboard_settings_heading_title', 'prefix_set_about_tab_title', 10, 2 );

 * Sets the help text for the 'About' settings tab.
 * @param string $help_text
 * @param string $tab
 * @return string Help text for tab with slug $tab
function prefix_set_about_tab_help_text( $help_text, $tab ) {
    if ( 'about' === $tab ) {
        $help_text = __( 'Personalize your store page by telling customers a little about yourself.' );

    return $help_text;

add_filter( 'dokan_dashboard_settings_helper_text', 'prefix_set_about_tab_help_text', 10, 2 );

Note: If you used an array key other than “about” when adding your menu item in Step 1, you will need to replace “about” with the key you used to make this snippet work.

As you can see, the heading_title and helper_text filters are almost identical. Each accepts the text to modify and a tab slug as arguments and returns the text to use for the given tab. In our case we target the “about” tab, setting the tab title to “About Me” and the tab text to “Personalize your store page by telling customers a little about yourself.”

After this step our settings page looks like this:

The result of step 2. Close, but where are the settings fields!?

Step 3: Outputting the tab content

A settings page is useless without settings fields. At this point, we’ll write a bit of code to output the content for our settings page. The code uses the dokan_render_settings_content action hook and looks like this:

 * Outputs the content for the 'About' settings tab.
 * @param array $query_vars WP query vars
function prefix_output_help_tab_content( $query_vars ) {
    if ( isset( $query_vars['settings'] ) && 'about' === $query_vars['settings'] ) {
        if ( ! current_user_can( 'dokan_view_store_settings_menu' ) ) {
            dokan_get_template_part ('global/dokan-error', '', [
                'deleted' => false,
                'message' => __( 'You have no permission to view this page', 'dokan-lite' )
            ] );
        } else {
            $user_id        = get_current_user_id();
            $bio            = get_user_meta( $user_id, 'prefix_bio', true );
            $birthdate      = get_user_meta( $user_id, 'prefix_birthdate', true );
            $favorite_color = get_user_meta( $user_id, 'prefix_favorite_color', true );

            <form method="post" id="settings-form"  action="" class="dokan-form-horizontal">
                <?php wp_nonce_field( 'dokan_about_settings_nonce' ); ?>

                <div class="dokan-form-group">
                    <label class="dokan-w3 dokan-control-label" for="bio">
                        <?php esc_html_e( 'Bio' ); ?>
                    <div class="dokan-w5">
                        <textarea class="dokan-form-control" name="bio" id="bio" placeholder="<?php esc_attr_e( 'Tell your story' ); ?>"><?php echo esc_html( $bio ); ?></textarea>
                        <p class="help-block"><?php esc_html_e( 'Tell your customers a little about yourself.' ); ?></p>

                <div class="dokan-form-group">
                    <label class="dokan-w3 dokan-control-label" for="birthdate">
                        <?php esc_html_e( 'Birthdate' ); ?>
                    <div class="dokan-w5">
                        <input class="dokan-form-control" type="date" name="birthdate" id="birthdate" value="<?php echo esc_attr( $birthdate ); ?>">

                <div class="dokan-form-group">
                    <label class="dokan-w3 dokan-control-label" for="favorite_color">
                        <?php esc_html_e( 'Favorite Color' ); ?>
                    <div class="dokan-w5">
                        <select class="dokan-form-control" name="favorite_color" id="favorite_color">
                            $colors = [
                                ''       => __( 'Select a color' ),
                                'red'    => __( 'Red' ),
                                'orange' => __( 'Orange' ),
                                'yellow' => __( 'Yellow' ),
                                'green'  => __( 'Green' ),
                                'blue'   => __( 'Blue' ),
                                'other'  => __( 'Other' ),

                            foreach ( $colors as $value => $label ) {
                                    '<option value="%s" %s>%s</option>',
                                    selected( $value, $favorite_color, false ),

                <div class="dokan-form-group">
                    <div class="dokan-w4 ajax_prev dokan-text-left" style="margin-left: 25%">
                        <input type="submit" name="dokan_update_about_settings" class="dokan-btn dokan-btn-danger dokan-btn-theme" value="<?php esc_attr_e( 'Update Settings' ); ?>">

                #settings-form {
                    margin-bottom: 0;

add_action( 'dokan_render_settings_content', 'prefix_output_help_tab_content' );

Note: If you used an array key other than “about” when registering your menu item, you’ll have to replace “about” with your chosen key to make this snippet work.

Let me highlight some of the important parts of this code:

    • The prefix_output_help_tab_content function accepts an array of WP query variables as input. On line 7, we check the value of the “settings” query variable to make sure that we’re only outputting our settings fields on the “About” page.
    • On line 8, we check whether the user has the capability required to view our settings page. If your settings page can be viewed by all vendors, you can eliminate this if statement.
    • Line 20 is where we start outputting our form fields. In your plugin or theme, you should probably extract the form markup to a separate template file, but for this example I’ve kept it inline.
      • Note that the ID of the form is set to settings-form. If any other ID is used, our logic for saving and validating our settings fields won’t work.
    • We output a nonce field on line 21. This will be important when we write our code for saving our settings fields in part 4.

We’re almost there! Let’s wrap up by writing some code to validate and save our settings fields.

Step 4: Saving and validating our settings

All of our hard work would be for naught if vendors can’t actually save their settings. To finish up, we’ll write a simple function to validate and save the values entered by the vendor.

There are a couple of approaches to this, but I prefer to use the dokan_store_profile_saved action hook. Here’s what the code looks like:

 * Saves the settings on the 'About' tab.
 * @param int $user_id ID of the user whose settings are being saved.
function prefix_save_about_settings( $user_id ) {
    // Bail if another settings tab is being saved
    if ( ! wp_verify_nonce( $_POST['_wpnonce'], 'dokan_about_settings_nonce' ) ) {

    $bio            = sanitize_text_field( $_POST['bio'] );
    $birthdate      = sanitize_text_field( $_POST['birthdate'] );
    $favorite_color = sanitize_text_field( $_POST['favorite_color'] );

    // Let's require that the user is 18 years of age or older
    $eighteen_years_ago = strtotime( '-18 years 00:00:00' );

    if ( $birthdate && strtotime( $birthdate ) > $eighteen_years_ago ) {
        wp_send_json_error( __( 'You must be at least eighteen years old - is your birthdate correct?' ) );

    update_user_meta( $user_id, 'prefix_bio', $bio );
    update_user_meta( $user_id, 'prefix_birthdate', $birthdate );
    update_user_meta( $user_id, 'prefix_favorite_color', $favorite_color );

add_action( 'dokan_store_profile_saved', 'prefix_save_about_settings' );

The most interesting part of this code is the validation logic starting on line 17. When the birthdate entered by the vendor is less than 18 years ago, we use wp_send_json_error() to send back an error message. You should use the same approach when you detect an error in your validation logic.

I should note that in a real world application you should also be doing client side validation (Dokan uses jQuery Validate). I’ve omitted client side validation to keep this example simple.

Now the values entered by the vendor will be saved, and we’ll get a helpful error message when we select an invalid birthdate. Nice!

The error displayed when an invalid birthdate is entered.

That’s it!

We now have a custom settings tab with basic validation. If you have any questions or suggestions, please feel free to leave a comment below!

The complete code for this tutorial can be found in this gist.


    1. Dokan doesn’t provide an API for creating menu items with submenus. Until this use case is supported, you can either avoid the use of submenus (e.g. by outputting your own navigation menu within your settings tab), or override the global/dokan-nav.php template and replace dokan_dashboard_nav() with a custom function that allows menu items other than “support” to have submenus.

  1. This is amazing, exactly what I was looking out for! I can’t wait to read future Extending Dokan articles by your team! I do have a question, I’m not super duper technical but what did you mean by the below? Could you please advise with an example? I tried to copy the codes you have provided into code snippets but it hasn’t worked yet!

    Note: The code examples in this article use the generic vendor prefix “prefix_” for function and meta key names. In addition, text domains are omitted wherever internationalization functions are used. You should replace the generic vendor prefix with your own prefix and insert your text domain where appropriate when incorporating these code snippets into your plugin or theme.

    Thanks so much!

    1. Hi Vita,

      I’m glad you found this article useful!

      As you may have noticed, all of the functions in the provided code snippets have a name that starts with “prefix_”. The quoted text is suggesting that you replace this generic prefix with your own unique prefix. For instance, if your marketplace is called MyMarket, you might rename the prefix_add_about_tab function from Step 1 to mymarket_add_about_tab. This is not strictly required but it’s advisable, especially if you plan to develop a plugin or theme for redistribution through or elsewhere. In your case it sounds like that isn’t your plan, so it’s not a huge deal if you skip this suggested step.

Leave a Reply

Your email address will not be published. Required fields are marked *