create custom shortcode wp and put php code in

the_permalink() echoes the output, and so does the_title() by default. So you should use get_permalink() and get_the_title():

$html .=  '<h3><a href="' . get_permalink() . '">' . get_the_title() . '</a></h3>';

Or use the_title() with the third parameter set to false to return and not echo the output:

$html .=  '<h3><a href="' . get_permalink() . '">' . the_title( '', '', false ) . '</a></h3>';

Update: The return $html; (in your original non-edited question) was actually outside the function and that would result the shortcode to give you no output! So make sure that it’s inside the function in your actual code. 🙂 And for secondary/custom WP_Query instances like your $loop variable, you just need to call wp_reset_postdata() and not wp_reset_query() since secondary queries don’t touch the (global) $wp_query variable. Unless of course, in your code, you modified that variable. But why would you do that.