If you want to load the post faster you can use JS to fetch your post, but on modern browsers probably preloading pages will be faster, the only drawback of preloading is that you'll consume storage for your visitors even if they don't visit the preloaded links, also with preloading links you will consume more bandwidth and resources on the server-side.

So if you want to stick with fetching with JS here is an example, but first, as a note, the example will not use the default REST API mostly because the default API only returns the content title and other attributes of the post and, we want to add things like Yoast data(without making two API request) also, in this case, will transfer some of the article template surrounding the content.

So first we start with adding a REST Endpoint:

add_action('rest_api_init', 'change_rest_post' );
function change_rest_post(){
   
  register_rest_route( 'a309/v1', '/get-post/(?P<id>\d+)/user/(?P&lt;user_id>\d+)', array(
    'methods' => 'GET',
    'callback' => 'get_post_by_id',
    'permission_callback' => '__return_true',
  ) );
}

Simple API takes two parameters, obvious the post-id, and the user-id. If you wonder why the user-id is passed is, for the comments section.

Next the callback function get_post_by_id:

add_action('rest_api_init', 'change_rest_post' );
function change_rest_post(){
   
  register_rest_route( 'a309/v1', '/get-post/(?P<id>\d+)/user/(?P&lt;user_id>\d+)', array(
    'methods' => 'GET',
    'callback' => 'get_post_by_id',
    'permission_callback' => '__return_true',
  ) );
}

There is some integration with yoast_seo plugin, also the a309_get_post_template is used also when getting more than one post then we need to get a list with excerpts. We return JSON and the template key is the node HTML of the entire article, which was outputted using the WP function get_template_part that in turn will get the file article.php, and for reference that file is:

add_action('rest_api_init', 'change_rest_post' );
function change_rest_post(){
   
  register_rest_route( 'a309/v1', '/get-post/(?P<id>\d+)/user/(?P&lt;user_id>\d+)', array(
    'methods' => 'GET',
    'callback' => 'get_post_by_id',
    'permission_callback' => '__return_true',
  ) );
}

Most of the unfamiliar functions are in general from plugins, or from template.

So now to the JS part, we should load this JS only on our index page:

add_action('rest_api_init', 'change_rest_post' );
function change_rest_post(){
   
  register_rest_route( 'a309/v1', '/get-post/(?P<id>\d+)/user/(?P&lt;user_id>\d+)', array(
    'methods' => 'GET',
    'callback' => 'get_post_by_id',
    'permission_callback' => '__return_true',
  ) );
}

There's a lot of code mostly because when we open a post we need to save our SEO head and the previous page in case the user hits the back button, we also need to set the new SEO head data when the post is opened, and we also need to add scripts/styles to the page that we didn't need on the previous page, plus we need to initialize any JS that we loaded dynamically after it loads. Also, we'll add a loader when we start and remove it after everything has loaded.

These days a load more button is less common since many websites use infinite scrolling, personally I don't thing that replacing every loading more button with an infinite scroll is desirable even if we talk about feeds.

Here's my load more posts function, you should also check if you have to call aditional JS to trigger plugins that you use:

add_action('rest_api_init', 'change_rest_post' );
function change_rest_post(){
   
  register_rest_route( 'a309/v1', '/get-post/(?P<id>\d+)/user/(?P&lt;user_id>\d+)', array(
    'methods' => 'GET',
    'callback' => 'get_post_by_id',
    'permission_callback' => '__return_true',
  ) );
}