TECHNICAL GUIDE

15 Useful LearnDash Hooks to Customize Your LMS

Learn how to use LearnDash actions and filters to streamline course progression, extend dashboards, and extract advanced progress reports.

PLUGIN LearnDash
HOOKS 15

LearnDash is the most popular WordPress LMS platform, but its default settings don't fit every business model. Dropping clean PHP hooks into a site-specific plugin is the best way to customize learning behaviors without editing core files. Whether you want to automate student navigation, secure course access, customize reporting, or add welcome widgets to the student dashboard, hooks are the cleanest way to do it.

Below are 15 useful LearnDash hooks divided into two main categories: Customize the LearnDash Student Experience (ideal for improving user engagement) and Power and Automate Your LMS Backend (ideal for custom reports and administrative controls).

1. learndash_course_completed

Redirect completed students to a custom thank you page.

Use case: LearnDash normally keeps students on the course page when they complete it. This redirect snippet routes them to a congratulations landing page, a survey form, or an upsell offer.

learndash_course_completed.php
add_action('learndash_course_completed', 'ppunch_redirect_on_course_complete', 10, 1);
function ppunch_redirect_on_course_complete($data) {
    // Define the custom landing page URL here
    $target_url = home_url('/course-complete-thank-you/');
    
    // Perform redirect if this is a standard browser request
    if (!wp_doing_ajax() && !wp_doing_cron() && !empty($target_url)) {
        wp_safe_redirect($target_url);
        exit;
    }
}
Explanation:

When the course completed hook fires, this script pulls the completion data and performs a safe WordPress redirect to your custom target page.

course completion redirect thank you page redirect learndash course completed

2. Auto-Enroll Onboarding Course

WHAT IT SOLVES:
Eliminates the manual work of assigning starter courses to new students. This snippet automatically assigns an onboarding course the moment a user registers on your site.
HOOKS: user_register

Auto-enroll new website registrations into a free onboarding course.

autoenroll-onboarding-course.php
add_action('user_register', 'ppunch_auto_enroll_onboarding_course', 10, 1);
function ppunch_auto_enroll_onboarding_course($user_id) {
    $onboarding_course_id = 999; // REPLACE with your onboarding Course Post ID
    
    if (function_exists('ld_update_course_access')) {
        ld_update_course_access($user_id, $onboarding_course_id);
    }
}
Explanation:

Hooks into the standard WordPress registration callback, checks if the LearnDash access update function is active, and assigns the target course ID to the new user.

auto-enroll new users learndash onboarding enrollment user register course assignment

3. learndash_quiz_completed (Lesson Completion)

Automatically mark a parent lesson complete when the student passes its quiz.

Use case: By default, LearnDash forces students to complete a quiz and then separately click a "Mark Complete" button on the lesson. Bypassing this step creates a smoother learning flow.

learndash_quiz_completed (Lesson Completion).php
add_action('learndash_quiz_completed', 'ppunch_complete_lesson_on_quiz_pass', 10, 2);
function ppunch_complete_lesson_on_quiz_pass($quiz_data, $user) {
    if (isset($quiz_data['pass']) && $quiz_data['pass'] == 1) {
        $quiz_id = $quiz_data['quiz']->ID;
        $user_id = $user->ID;
        
        // Fetch parent lesson and course IDs
        $lesson_id = learndash_get_setting($quiz_id, 'lesson');
        $course_id = learndash_get_setting($quiz_id, 'course');
        
        if (!empty($lesson_id) && !empty($course_id) && function_exists('learndash_process_mark_complete')) {
            learndash_process_mark_complete($user_id, $lesson_id, false, $course_id);
        }
    }
}
Explanation:

This snippet listens for quiz completions. If the student passed, it grabs the parent lesson and parent course IDs and programmatically saves the lesson progress.

auto-complete lesson pass quiz complete lesson learndash quiz completion progress

4. wp_login (Expiry Checks)

Send renewal emails to students whose course access is expiring soon.

Use case: Instead of losing members when access ends, this warning email notifies them a few days prior, driving renewal conversions.

wp_login (Expiry Checks).php
add_action('wp_login', 'ppunch_check_expiring_courses_on_login', 10, 2);
function ppunch_check_expiring_courses_on_login($user_login, $user) {
    if (!function_exists('learndash_user_get_enrolled_courses')) {
        return;
    }
    
    $user_id = $user->ID;
    $enrolled_courses = learndash_user_get_enrolled_courses($user_id);
    
    if (empty($enrolled_courses)) {
        return;
    }
    
    foreach ($enrolled_courses as $course_id) {
        $access_from = ld_course_access_from($course_id, $user_id);
        $course_duration = learndash_get_course_access_duration($course_id, $user_id);
        
        if ($access_from > 0 && $course_duration > 0) {
            $expire_date = $access_from + $course_duration;
            $time_left = $expire_date - time();
            $days_left = floor($time_left / DAY_IN_SECONDS);
            
            // Send warning email if expiring within 5 days
            if ($days_left > 0 && $days_left <= 5) {
                $to = $user->user_email;
                $subject = "Your Course Access is Expiring Soon!";
                $message = "Hello " . esc_html($user->display_name) . ",\n\nYour access to the course '" . get_the_title($course_id) . "' expires in " . $days_left . " days. Renew now to maintain access.";
                wp_mail($to, $subject, $message);
            }
        }
    }
}
Explanation:

Triggers upon user login. It loops through the user's courses, checks their course access duration against their start timestamp, and sends a renewal email if they have 5 days or fewer remaining.

course expiration notification renew course access email learndash access expiry check

5. init (Reset Shortcode)

Add a "Reset Progress" button allowing students to clear their own course data.

Use case: Useful for training sites where users need to clear their logs to practice or retake compliance modules. Usually only admins can reset progress; this gives control to the student.

init (Reset Shortcode).php
add_action('init', 'ppunch_register_progress_reset_shortcode');
function ppunch_register_progress_reset_shortcode() {
    add_shortcode('ppunch_ld_reset_progress', 'ppunch_render_progress_reset_button');
}

function ppunch_render_progress_reset_button($atts) {
    $atts = shortcode_atts(['course_id' => 0], $atts, 'ppunch_ld_reset_progress');
    $course_id = intval($atts['course_id']);
    
    if (!$course_id || !is_user_logged_in()) {
        return '';
    }
    
    $user_id = get_current_user_id();
    
    // Handle Reset Action
    if (isset($_POST['ppunch_reset_nonce']) && wp_verify_nonce($_POST['ppunch_reset_nonce'], 'ppunch_reset_course_' . $course_id)) {
        delete_user_meta($user_id, '_sfwd-course_progress');
        delete_user_meta($user_id, 'course_completed_' . $course_id);
        
        wp_safe_redirect(get_permalink($course_id));
        exit;
    }
    
    ob_start();
    ?>
    <form method="post" onsubmit="return confirm('Are you sure you want to reset your progress? This cannot be undone.');">
        <?php wp_nonce_field('ppunch_reset_course_' . $course_id, 'ppunch_reset_nonce'); ?>
        <button type="submit" class="btn-primary" style="background-color: #d9534f; color: #fff; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer;">
            Reset Course Progress
        </button>
    </form>
    <?php
    return ob_get_clean();
}
Explanation:

Registers a shortcode [ppunch_ld_reset_progress course_id="123"] which renders a secure button. If clicked, it deletes the user's course progress metadata and refreshes the course layout.

reset course progress shortcode clear student progress button restart learndash course

6. ld_lesson_access_from

Release lessons based on the student's registration date.

Use case: LearnDash only drips lessons relative to the course enrollment date. This filter changes the calculation to drip lessons based on when the user registered their account globally.

ld_lesson_access_from.php
add_filter('ld_lesson_access_from', 'ppunch_drip_by_registration_date', 10, 3);
function ppunch_drip_by_registration_date($access_from, $post_id, $user_id) {
    $user = get_userdata($user_id);
    if (!$user) {
        return $access_from;
    }
    
    $registration_timestamp = strtotime($user->user_registered);
    $drip_days = learndash_get_setting($post_id, 'visible_after');
    
    if ($drip_days > 0) {
        // visibility offset is calculated from user registration
        return $registration_timestamp + ($drip_days * DAY_IN_SECONDS);
    }
    
    return $access_from;
}
Explanation:

Intercepts the access date calculation, retrieves the user's registered timestamp, and offset-adds the lesson's visibility days to build the custom drip date.

registration based drip content cohort drip lessons invisible after lesson release date

7. learndash_quiz_completed (Course Completion)

Automatically complete a course when a student passes the final exam.

Use case: Ideal for compliance certificates or fast-track courses. If a student already knows the material and passes the final exam quiz, this marks the course complete instantly.

learndash_quiz_completed (Course Completion).php
add_action('learndash_quiz_completed', 'ppunch_test_out_course_on_final_pass', 10, 2);
function ppunch_test_out_course_on_final_pass($quiz_data, $user) {
    if (isset($quiz_data['pass']) && $quiz_data['pass'] == 1) {
        $quiz_id = $quiz_data['quiz']->ID;
        $user_id = $user->ID;
        
        // Check custom meta to see if this is marked as a Final Exam quiz
        $is_final_exam = get_post_meta($quiz_id, '_ppunch_is_final_exam', true);
        $course_id = learndash_get_setting($quiz_id, 'course');
        
        if ($is_final_exam && !empty($course_id) && function_exists('learndash_process_mark_complete')) {
            learndash_process_mark_complete($user_id, $course_id);
        }
    }
}
Explanation:

Checks if the finished quiz is flagged as a final exam via custom metadata. If yes and passed, it calls the course completion processor.

final exam course completion skip course steps quiz pass test out course learndash

8. learndash_course_completed (Course Chaining)

Auto-enroll students into the next course on completion.

Use case: Keep students engaged in your training path without forcing them to manually purchase or search for the next module.

learndash_course_completed (Course Chaining).php
add_action('learndash_course_completed', 'ppunch_chain_course_enrollment', 10, 1);
function ppunch_chain_course_enrollment($data) {
    $user_id = $data['user']->ID;
    $completed_course_id = $data['course']->ID;
    
    // completed course ID => next course ID
    $course_chain = [
        101 => 102, // E.g., when completing 101, enroll in 102
        201 => 202,
    ];
    
    if (isset($course_chain[$completed_course_id]) && function_exists('ld_update_course_access')) {
        $next_course_id = $course_chain[$completed_course_id];
        ld_update_course_access($user_id, $next_course_id);
    }
}
Explanation:

Listens for course completion, matches the completed course ID against your defined curriculum chain map, and assigns the next course ID to the student.

course chaining enrollments linear course navigation path auto enroll next course

9. template_redirect

Automatically mark lessons and topics complete when viewed.

Use case: For resource libraries or reference sites, requiring users to click "Mark Complete" on every page is tedious. This completes lessons automatically as soon as they read them.

template_redirect.php
add_action('template_redirect', 'ppunch_auto_complete_step_on_view');
function ppunch_auto_complete_step_on_view() {
    if (!is_user_logged_in() || !function_exists('learndash_get_course_id')) {
        return;
    }
    
    if (is_singular(['sfwd-lessons', 'sfwd-topics'])) {
        $step_id = get_the_ID();
        $user_id = get_current_user_id();
        $course_id = learndash_get_course_id($step_id);
        
        if ($course_id && function_exists('learndash_process_mark_complete') && function_exists('learndash_is_item_complete')) {
            $is_completed = learndash_is_item_complete($step_id, $user_id, $course_id);
            
            if (!$is_completed) {
                learndash_process_mark_complete($user_id, $step_id, false, $course_id);
            }
        }
    }
}
Explanation:

Triggers when loading single lessons or topics. It verifies if the step is not yet completed, and executes the complete progress handler immediately.

auto complete step page view trigger mark complete on visit learndash clickless navigation

10. learndash_show_quiz_take_button

Prevent students from retaking a quiz once they have passed it.

Use case: Critical for compliance training and certifiable modules. Prevents users from overriding their original passing score or resetting attempts to guess answers.

learndash_show_quiz_take_button.php
add_filter('learndash_show_quiz_take_button', 'ppunch_prevent_quiz_retakes_after_pass', 10, 3);
function ppunch_prevent_quiz_retakes_after_pass($show_button, $user_id, $quiz_id) {
    if (!$show_button || empty($user_id)) {
        return $show_button;
    }
    
    // Retrieve past user attempts
    $quiz_attempts = get_user_meta($user_id, '_sfwd-quizzes', true);
    
    if (!empty($quiz_attempts) && is_array($quiz_attempts)) {
        foreach ($quiz_attempts as $attempt) {
            // Block start button if already passed once
            if ($attempt['quiz'] == $quiz_id && isset($attempt['pass']) && $attempt['pass'] == 1) {
                return false;
            }
        }
    }
    
    return $show_button;
}
Explanation:

Looks up historical user quiz data for this quiz ID. If a passing entry is found, the filter returns false, which hides the quiz take/restart button.

lock quiz after pass compliance quiz attempt limit block retake once passed

11. learndash_shortcode_profile_before_template

Inject custom stats cards at the top of the student dashboard.

Use case: The default LearnDash student profile is plain. Adding custom statistics like enrolled vs. completed courses makes the student dashboard look highly professional.

learndash_shortcode_profile_before_template.php
add_action('learndash_shortcode_profile_before_template', 'ppunch_inject_student_stats_header', 10, 1);
function ppunch_inject_student_stats_header($user_id) {
    if (empty($user_id)) {
        return;
    }
    
    $enrolled = learndash_user_get_enrolled_courses($user_id);
    $completed = 0;
    
    if (!empty($enrolled)) {
        foreach ($enrolled as $course_id) {
            if (learndash_course_status($course_id, $user_id) === 'completed') {
                $completed++;
            }
        }
    }
    
    $total_enrolled = count($enrolled);
    
    ?>
    <div class="learndash-profile-stats-grid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-bottom: 30px;">
        <div class="stat-card" style="background: #fff; padding: 20px; border-radius: 8px; border: 1px solid rgba(0,0,0,0.05); text-align: center;">
            <span style="font-size: 0.85rem; color: #666; text-transform: uppercase; font-weight: 600; display: block; margin-bottom: 5px;">Enrolled Courses</span>
            <strong style="font-size: 2rem; color: #333;"><?php echo esc_html($total_enrolled); ?></strong>
        </div>
        <div class="stat-card" style="background: #fff; padding: 20px; border-radius: 8px; border: 1px solid rgba(0,0,0,0.05); text-align: center;">
            <span style="font-size: 0.85rem; color: #666; text-transform: uppercase; font-weight: 600; display: block; margin-bottom: 5px;">Completed Courses</span>
            <strong style="font-size: 2rem; color: #44b549;"><?php echo esc_html($completed); ?></strong>
        </div>
    </div>
    <?php
}
Explanation:

Appends custom HTML cards directly above the [ld_profile] output, fetching course counts using user enrollment functions.

custom profile header stats ld_profile summary columns student dashboard customize

12. Custom Student Profile Columns

WHAT IT SOLVES:
LearnDash's default student profile table only displays generic quiz dates. This snippet allows site owners to dynamically inject custom columns (like "Requirements Status") and fill them with color-coded pass/fail tags.
HOOKS: learndash_profile_quiz_columns learndash_profile_quiz_row_data

Add custom columns and populate data in the student profile quiz history table.

custom-student-profile-columns.php
// 1. Add Custom Column Header
add_filter('learndash_profile_quiz_columns', 'ppunch_add_profile_quiz_custom_column', 10, 1);
function ppunch_add_profile_quiz_custom_column($columns) {
    $columns['ppunch_status'] = 'Requirements';
    return $columns;
}

// 2. Render Custom Row Cell Value
add_action('learndash_profile_quiz_row_data', 'ppunch_render_profile_quiz_custom_column_cells', 10, 3);
function ppunch_render_profile_quiz_custom_column_cells($column_key, $user_id, $quiz_attempt) {
    if ($column_key === 'ppunch_status') {
        $pass_status = isset($quiz_attempt['pass']) && $quiz_attempt['pass'] == 1 ? 'Passed' : 'Failed';
        $color = $pass_status === 'Passed' ? '#44b549' : '#d9534f';
        echo '<span style="color: ' . $color . '; font-weight: 600;">' . esc_html($pass_status) . '</span>';
    }
}
Explanation:

First, hooks into the columns filter to register the custom column header. Second, hooks into the row data callback to render custom HTML cells matching the column key.

custom quiz table columns render quiz history cell learndash profile quiz data

13. Custom CSV Administrative Exports

WHAT IT SOLVES:
Corporate partners need user enrollment statistics exported with custom fields (like user company domain). This snippet automatically appends a dynamic column header and hydrates it with user email domain info during administrative exports.
HOOKS: learndash_data_reports_headers learndash_data_reports_line

Add custom fields (like user company domain) to LearnDash admin CSV report exports.

custom-csv-administrative-exports.php
// 1. Add Custom CSV Export Header
add_filter('learndash_data_reports_headers', 'ppunch_add_custom_reporting_csv_headers', 10, 2);
function ppunch_add_custom_reporting_csv_headers($headers, $report_type) {
    if ($report_type === 'user-courses') {
        $headers['company_domain'] = 'User Domain';
    }
    return $headers;
}

// 2. Populate CSV Row Data
add_filter('learndash_data_reports_line', 'ppunch_hydrate_reporting_csv_rows', 10, 3);
function ppunch_hydrate_reporting_csv_rows($row, $raw_data, $report_type) {
    if ($report_type === 'user-courses') {
        $user_id = $raw_data->user_id ?? 0;
        $domain = '';
        
        if ($user_id) {
            $user = get_userdata($user_id);
            if ($user) {
                $email_parts = explode('@', $user->user_email);
                $domain = end($email_parts);
            }
        }
        
        $row['company_domain'] = $domain;
    }
    return $row;
}
Explanation:

Intercepts LearnDash's course reporting export initialization and appends your custom header label to the user-courses report.

custom reporting csv headers export user domain csv learndash course export filter

14. Alphabetical Lesson Topic Ordering

WHAT IT SOLVES:
If lessons have dozens of reference materials or glossary topics, ordering them manually using LearnDash's drag-and-drop builder is tedious. This filter automatically orders topics alphabetically.
HOOKS: learndash_lesson_topics_list_sort

Sort lesson topics alphabetically by title instead of drag-and-drop order.

alphabetical-lesson-topic-ordering.php
add_filter('learndash_lesson_topics_list_sort', 'ppunch_sort_topics_alphabetically', 10, 2);
function ppunch_sort_topics_alphabetically($sort_args, $lesson_id) {
    $sort_args['orderby'] = 'title';
    $sort_args['order'] = 'ASC';
    return $sort_args;
}
Explanation:

Intercepts the topic list query parameters inside LearnDash and overrides the default sorting parameters to sort by title in ascending order.

sort topics alphabetically custom topic navigation order learndash topic list sorting

15. Default Quiz Passing Threshold

WHAT IT SOLVES:
Manually setting the passing score on dozens of quizzes is prone to user error. This filter automatically sets a default passing threshold of 75% for all quizzes on creation.
HOOKS: learndash_quiz_passing_percentage

Automatically apply a default passing score percentage to quizzes.

default-quiz-passing-threshold.php
add_filter('learndash_quiz_passing_percentage', 'ppunch_set_default_quiz_passing_score', 10, 2);
function ppunch_set_default_quiz_passing_score($passing_percentage, $quiz_id) {
    // Autoset default passing threshold to 75% if not yet configured
    return empty($passing_percentage) ? 75 : $passing_percentage;
}
Explanation:

Filters the passing percentage setting of LearnDash quizzes, applying a fallback percentage of 75% if a custom score has not been configured.

quiz passing score default quiz settings learndash passing percentage override
KNOWLEDGE BASE

Common Questions

Yes. We regularly build custom registration paths, WooCommerce auto-enrollment customizations, and multi-tier subscription checkouts. Get in touch with us!
Absolutely. We engineer custom synchronization bridges connecting LearnDash with CRMs like HubSpot, ActiveCampaign, or custom databases to track learning metrics. Get in touch with us!
You can add them to a site-specific plugin or your child theme's functions.php file. A code manager like WPCode is also a great option. No LearnDash core files are edited.
No. Hooks operate outside of LearnDash's core codebase, meaning your custom logic remains completely intact and safe during core updates.
Yes. We specialize in troubleshooting complex LMS configurations, resolving hook conflicts, and performance optimization. Get in touch with us!
// AUTHOR Muneeb K. Lead WordPress Developer

16 years building custom WordPress plugins for WooCommerce, MemberPress, and LearnDash platforms — 500+ projects delivered. Focused on performance, stability, and clean architecture.

MemberPress Expertise

Need custom LearnDash features?

Contact our team for a free technical audit and let us build the hooks that scale your learning platform.

// TECH STACK

Tools We Master
Inside Out.

Starting with WordPress at the core, we use a curated stack of powerful tools to build fast, stable, and scalable systems for your business.