How to Customize LifterLMS (Without Breaking Updates)

Put LifterLMS changes where an update cannot overwrite them: settings first, a child theme for templates and CSS, and a small plugin for hooks. Includes hook snippets and a pre-update checklist.

  • Add as a preferred source on Google
Illustration for “How to Customize LifterLMS (Without Breaking Updates)”

Sooner or later a LifterLMS site needs something the settings do not offer: a different course layout, an extra field at checkout, a renamed tab on the Student Dashboard. The plugin is built to be changed. What matters is where you put the change.

Edit a file inside wp-content/plugins/lifterlms/ and the next update overwrites your work. Put the same change in the right place and it keeps working. This tutorial shows where that place is for each kind of customization.

You need to be comfortable with a little PHP and CSS, and you should try everything on a staging copy first.

Start with settings before code

A lot of "customization" is already a setting, and a setting survives updates.

  • Hide a Student Dashboard tab. Go to LifterLMS > Settings > Accounts and clear the tab's slug in the Student Dashboard Endpoints section. A blank endpoint disables it.
  • Change checkout, registration or account fields. Open the form under LifterLMS > Forms. Each one is edited in the block editor.
  • Rearrange a course page. On a course edited in the block editor, the syllabus, pricing table, instructors and progress bar are blocks you can move or delete.

Child theme or small custom plugin?

Both are upgrade-safe. The difference is what happens when you change themes.

Kind of changeWhere it belongs
Template overridesChild theme, in a lifterlms/ folder
CSSChild theme stylesheet
Layout tweaks tied to this themeChild theme functions.php
Enrollment rules, automations, custom fields, integrationsA small custom plugin

The rule of thumb: if it should survive a redesign, it goes in a plugin. WordPress documents how to create a child theme, and a custom plugin can start as a single PHP file with a header comment. When the snippets start to pile up, read when to build a custom LifterLMS plugin.

Template overrides: where they go and how to keep them current

LifterLMS builds its front-end output from PHP templates in wp-content/plugins/lifterlms/templates/. To change one:

  1. Find the template. Each lesson row in the course syllabus, for example, is templates/course/lesson-preview.php.
  2. Create a lifterlms folder in your child theme.
  3. Copy the file into it, keeping the sub-folders but dropping templates/. The result is wp-content/themes/your-child-theme/lifterlms/course/lesson-preview.php.
  4. Edit your copy.

LifterLMS looks in the child theme, then the parent theme, then falls back to its own file. The LifterLMS template guide walks through the same steps.

The cost is that your copy is frozen. When LifterLMS later fixes a bug or improves accessibility in that template, your override does not get the fix. So:

  • Override the smallest template that does the job, and use a hook instead if one exists.
  • Keep the @version line at the top of the copied file. LifterLMS > Status > System Report lists your overrides with their version beside the current core version.
  • At each update, read the "Updated Templates" list in the LifterLMS changelog and re-apply your edits to a fresh copy of anything on it.
  • If the site uses Redis or Memcached, flush the object cache after creating the lifterlms folder for the first time. LifterLMS caches the list of override folders for up to a day.
  • On a block theme, the Course Catalog and Membership Catalog are block templates. Edit them in the Site Editor; those edits are stored in the database, not in files.

Hooks: three snippets to start from

Hooks add or change behavior without copying a template. Put these in your custom plugin or child theme functions.php. The acme_ prefix and the acme text domain are placeholders for your own.

Add content to the course page

add_action( 'lifterlms_single_course_after_summary', 'acme_course_support_note', 95 );

function acme_course_support_note() {
	if ( ! llms_is_user_enrolled( get_current_user_id(), get_the_ID() ) ) {
		return;
	}
	echo '<p>' . esc_html__( 'Stuck on a lesson? Reply to your welcome email and we will help.', 'acme' ) . '</p>';
}

Enrolled students see the note; everyone else sees nothing. On a course built in the classic editor, priority 95 places it after the syllabus (90) and before reviews (100).

One caveat. On courses and lessons edited in the block editor, LifterLMS removes its own default callbacks for the syllabus, pricing table, instructors, progress and lesson navigation, because those are blocks in the content. Your note still prints, after the blocks, but a remove_action() aimed at those defaults does nothing. Delete or move the block instead.

Rename a Student Dashboard tab

add_filter( 'llms_get_student_dashboard_tabs', 'acme_rename_courses_tab' );

function acme_rename_courses_tab( $tabs ) {
	if ( isset( $tabs['view-courses'] ) ) {
		$tabs['view-courses']['title'] = __( 'My Programs', 'acme' );
	}
	return $tabs;
}

Enroll students in a follow-up course

add_action( 'lifterlms_course_completed', 'acme_enroll_in_next_course', 10, 2 );

function acme_enroll_in_next_course( $student_id, $course_id ) {
	$next = array(
		123 => 456, // Finished course ID => next course ID.
	);
	if ( isset( $next[ $course_id ] ) ) {
		llms_enroll_student( $student_id, $next[ $course_id ], 'acme_course_sequence' );
	}
}

The third argument is a free-text label stored as the enrollment trigger, which helps later when you need to know why someone has access. The LifterLMS code reference lists hooks and functions with their parameters.

CSS

LifterLMS front-end classes start with llms-, such as .llms-syllabus-wrapper, .llms-lesson-preview and .llms-button-action. Put your rules in the child theme stylesheet or the theme's Additional CSS panel, never in the plugin's own CSS files.

.llms-syllabus-wrapper .llms-lesson-preview .llms-lesson-title {
	font-weight: 600;
}

LifterLMS buttons also carry the WordPress wp-element-button class, so on a block theme they pick up the button styles you set for the theme. Check that before writing button CSS by hand.

Custom fields on registration and checkout

You have three options, in order of effort:

  • Built-in fields. Name, address and phone fields are already in the forms under LifterLMS > Forms. Reorder them or change which are required there.
  • The Custom Fields add-on. This paid LifterLMS add-on puts extra field types, such as text, dropdown and checkboxes, into the same editor.
  • Code. The lifterlms_get_person_fields filter appends fields to the forms.
add_filter( 'lifterlms_get_person_fields', 'acme_add_company_field' );

function acme_add_company_field( $fields ) {
	$fields[] = array(
		'id'       => 'acme_company',
		'name'     => 'acme_company',
		'type'     => 'text',
		'label'    => __( 'Company', 'acme' ),
		'required' => false,
	);
	return $fields;
}

The value is saved to user meta under the field's name. The filter also passes the form location (checkout, registration or account) as a second parameter. If a field should appear on only some forms, register the filter with two accepted arguments and check it. To act on a new signup, hook lifterlms_user_registered, which receives the user ID, the submitted data and the form location.

Pre-update checklist

  1. Back up the database and files, or take a host snapshot.
  2. Read the changelog for every version you are skipping. Look for deprecations and updated templates.
  3. Compare that list with the overrides in LifterLMS > Status > System Report and refresh any that changed.
  4. Update on staging first with WP_DEBUG and WP_DEBUG_LOG enabled, then check debug.log for deprecation notices that point at your own code.
  5. Test a purchase, an enrollment, a lesson completion, a quiz and the Student Dashboard.
  6. Update production at a quiet time and repeat the purchase test.

What to do next

For each change on your list, pick the lightest tool that works: a setting, then a block, then CSS, then a hook, and a template override only when nothing else will do. Keep every snippet in version control with a comment saying why it exists.

It is worth getting help when a change touches checkout or access rules, when overrides have piled up and updates have become stressful, or when you have inherited a site where someone edited the plugin directly. Our LifterLMS customization service handles that kind of work, ongoing LifterLMS support covers updates, and you can ask us a question any time. For the bigger picture, see our guide to LifterLMS development.

Add LifterLMS Expert to your preferred sources

Add lifterlmsexpert as a preferred source on Google, or open this article in your AI assistant to use it as a source.

  • Add as a preferred source on Google
VishavjeetChoubeyLifterLMS Expert
Free consultation

Prefer expert help over DIY?

Skip the trial and error. Get specialist LifterLMS help and ship faster.

Talk to a LifterLMS Expert Explore our services

Replies within one business day