Markdown and HTML Mixed Usage Complete Guide

Markdown is a joy to write, but you'll inevitably run into formatting needs it can't handle — like centering an image, changing text color, creating collapsible sections, or building tables with rowspan/colspan. That's when you might search for "markdown in html" or "html in markdown" to see if these two formats can be mixed.

The answer is yes. Markdown's creator, John Gruber, explicitly stated in his original design: HTML is Markdown's publishing format. Markdown is not a replacement for HTML, but a supplement to it. So writing HTML tags directly in a Markdown file isn't a hack — it's an officially encouraged practice.

However, there are quite a few details worth knowing. For example, inline and block-level tags behave very differently. Some platforms support the markdown="1" attribute to let Markdown syntax work inside HTML tags, while others completely ignore it. In this article, I'll explain these rules and differences clearly, and share some practical tips from real-world usage.

Markdown HTML mixed usage diagram

Core Rules: Inline Tags vs. Block-level Tags

The key to understanding Markdown and HTML interop is knowing how these two types of HTML tags behave differently within Markdown.

Inline Tags — Use Freely, Markdown Parses Normally

Inline tags (also called span-level tags) include <span>, <em>, <strong>, <a>, <code>, <br>, and others. These tags can be used anywhere within Markdown text and do not affect how the surrounding Markdown syntax is parsed.

This is an example of <span style="color:red">red text</span>.

In this sentence, <em>this uses HTML italic</em> while **this uses Markdown bold** — both coexist peacefully.

Click <a href="https://example.com" target="_blank">this link</a> to open in a new window.

The above examples work correctly in virtually every Markdown parser, because inline tags are treated as part of the Markdown text.

Block-level Tags — Markdown Inside Is Ignored

Block-level tags are a different story. When <div>, <table>, <p>, <pre>, or <form> tags appear, the Markdown parser considers this area to no longer be Markdown, and treats the content as raw HTML.

If you write it like this, **bold** won't work:

<div>
The **bold** in this paragraph won't be rendered, and *italic* won't either.
</div>

This behavior is explicitly defined in the Markdown specification (both the original spec and CommonMark) — it's not a parser bug. When the Markdown parser encounters a block-level HTML tag, it stops parsing and outputs the raw HTML as-is.

I first ran into this issue when writing a GitHub README. I was trying to use <div> for a two-column layout, only to find that all Markdown links and formatting inside the div had stopped working and turned into plain text. I later learned this was by design in Markdown parsers.

Separate Block-level Tags from Markdown Content with Blank Lines

One easily overlooked rule: block-level HTML tags must be separated from Markdown content with blank lines before and after. Without blank lines, some parsers will treat the HTML tags as plain text.

<!-- Correct: blank lines before and after -->

Previous Markdown content.

<div>
<p>HTML block-level content</p>
</div>

Following Markdown content.

Also, block-level HTML tags should not be indented with spaces or tabs. If indented by 4 spaces or 1 tab, Markdown will treat them as code blocks.

Common Use Cases: Using HTML for What Markdown Can't Do

Since Markdown supports embedding HTML, what are the specific scenarios where you'd need it? Here are the most common ones.

Controlling Image Size and Position

Markdown's image syntax ![alt](url) has no way to specify dimensions or alignment. If you want to control the display size or center an image, you need to use HTML.

<!-- Set image width -->
<img src="image.png" width="300" alt="Example image">

<!-- Center an image -->
<div align="center">
<img src="image.png" width="80%" alt="Centered image">
</div>

On GitHub, the width and height attributes of the <img> tag are well supported. However, the align attribute is legacy HTML and may be ignored by some platforms. A more modern approach is to use the CSS style attribute.

Text Color and Styles

Markdown has no syntax for setting text color. You can use <span> with style to achieve this:

<span style="color: red;">Red text</span>
<span style="color: #0066cc; font-size: 18px;">Blue large text</span>
<span style="background-color: yellow;">Yellow highlighted text</span>

Admittedly, this looks a bit awkward in a plain Markdown editor, but the rendered output works fine. My advice: if you only occasionally need to change a color, <span> is sufficient; if an entire document requires complex style control, you should probably write HTML directly instead of Markdown.

Collapsible Content (details/summary)

The <details> and <summary> HTML tags are well supported on GitHub, GitLab, and similar platforms. They create collapsible/expandable content sections, which are perfect for supplementary information or long code blocks.

<details>
<summary>Click to expand and view the detailed code</summary>

```python
def hello():
    print("Hello, World!")


Note that the text inside the `<summary>` tag will be displayed as the heading of the collapsible section. This technique is especially useful in README documents — folding installation steps or FAQs makes the page look much cleaner.

### Keyboard Key Styling

If you're writing technical documentation, you might need to display keyboard shortcuts. The `<kbd>` tag is perfect for this:

```markdown
Press <kbd>Ctrl</kbd> + <kbd>C</kbd> to copy, <kbd>Ctrl</kbd> + <kbd>V</kbd> to paste.

Most Markdown renderers display <kbd> as a small bordered box that looks like a real keyboard key.

In-page Anchor Jump

Markdown's link syntax [text](#anchor) supports in-page jumping, but you need to define the anchor position first. You can use HTML's id attribute to achieve this:

<div id="section-1"></div>

## First Section

Here is the section content...

[Jump to the first section](#section-1)

On GitHub, headings automatically generate anchors (based on the heading text), so this technique is mainly useful for non-heading positions.

Advanced Techniques: Making Markdown Work Inside HTML Tags

As mentioned earlier, Markdown syntax inside block-level HTML tags is not parsed by default. But there are scenarios where you genuinely need to write Markdown inside HTML tags — for example, using <div> for layout while still wanting Markdown links and formatting.

Option 1: Add Blank Lines (CommonMark Spec)

The CommonMark specification (used by GitHub, GitLab, and many modern parsers) supports a method: adding blank lines between HTML tags and content, signaling to the parser that the content should be treated as Markdown.

<div>

The **bold** in this paragraph will render correctly.

- List item one
- List item two

</div>

The key is having blank lines after the opening tag <div> (before the content) and before the closing tag </div> (after the content). I've tested this and it works on GitHub.

Option 2: The markdown Attribute (Python-Markdown, Markdown Extra)

Python-Markdown's md_in_html extension and PHP Markdown Extra support adding a markdown attribute to HTML tags, explicitly telling the parser to treat the inner content as Markdown.

<div markdown="1">

The **Markdown** here will render correctly.

</div>

The markdown attribute supports three values:

ValueEffect
"1"Use the tag's default behavior (block mode for div, span mode for p)
"block"Force block mode — paragraphs, headings, and lists all render normally
"span"Force inline mode — only inline syntax like bold, italic, and links render

Note that this attribute is not part of the standard Markdown specification and only works on parsers that support it. For example, GitHub's renderer doesn't support this attribute (at least not currently), but GitHub Pages' Jekyll/Kramdown environment does.

Option 3: Use <span> Instead of <div>

Since Markdown inside inline tags is unaffected, a workaround is to use <span> instead of <div> and make it behave as a block element via CSS.

<span style="display: block; border: 1px solid #ccc; padding: 10px;">

The **Markdown** here will render correctly because span is an inline tag.

</span>

This method has good compatibility, but it's not semantically rigorous — <span> is semantically an inline element, and forcing it to block-level isn't great for accessibility.

Platform Compatibility Comparison

Different platforms vary greatly in their support for HTML in Markdown. I've compiled the comparison table below based on actual testing and official documentation.

FeatureGitHubGitLabJekyll/KramdownPython-MarkdownObsidianTypora
Inline HTML tagsYesYesYesYesYesYes
Block-level HTML tagsYesYesYesYesYesYes
Blank lines inside block tags for Markdown parsingYesYesYesNoNoPartial
markdown="1" attributeNoNoYesYes (extension)NoNo
<details>/<summary>YesYesYesYesYesYes
<img> width/heightYesYesYesYesYesYes
<span style="color:">YesYesYesYesYesYes
<iframe>NoNoYesYesPartialYes
<script>NoNoNoNoNoNo

Data sources: Official documentation of each platform + top-voted Stack Overflow answers + hands-on testing. GitHub's HTML whitelist can be found in the github/markup project.

A few noteworthy points:

  • GitHub, for security reasons, maintains a whitelist of allowed HTML tags. Tags like <script>, <iframe>, and <style> are filtered out directly.
  • Obsidian uses its own Markdown parser, and its HTML support has some unique characteristics. For example, you can't use Markdown syntax inside HTML blocks in Obsidian, but you can achieve this indirectly through the DataviewJS plugin.
  • Jekyll's Kramdown parser has the most comprehensive support for HTML in Markdown — both the markdown="1" attribute and the blank-line approach inside block-level tags work.

Practical Application Scenarios and Examples

Scenario 1: Centering Images in a GitHub README

This is a pattern I frequently use in open-source projects:

<div align="center">

![Project Logo](logo.png)

**A concise project description**

[![GitHub Stars](https://img.shields.io/github/stars/user/repo)](https://github.com/user/repo)

</div>

On GitHub, this works correctly because the blank line after <div align="center"> tells the parser to treat the content as Markdown. By the way, while <div align="center"> uses the deprecated align attribute, it's the most reliable centering method on GitHub.

Scenario 2: Embedding Complex Tables in Markdown

Markdown tables don't support rowspan/colspan. When you need those effects, use HTML tables directly:

<table>
  <tr>
    <th>Feature</th>
    <th>Free Plan</th>
    <th>Pro Plan</th>
  </tr>
  <tr>
    <td rowspan="2">Storage</td>
    <td>5GB</td>
    <td>100GB</td>
  </tr>
  <tr>
    <td colspan="2">Expandable to 1TB (Pro only)</td>
  </tr>
</table>

Scenario 3: Multi-column Layout with CSS

On platforms that support the style attribute (like your own blog or documentation site), you can use CSS for simple multi-column layouts:

<div style="display: flex; gap: 20px;">

<div style="flex: 1;">

### Left Column

- Point one
- Point two

</div>

<div style="flex: 1;">

### Right Column

- Point three
- Point four

</div>

</div>

However, this approach may not work on GitHub, as GitHub filters out certain CSS rules from the style attribute.

Frequently Asked Questions

Can I Write CSS Directly in a Markdown File?

Yes, but support is limited. You can use <style> tags to define styles, but many platforms (especially GitHub) will filter them out. It usually works fine on your own blog or documentation site:

<style>
.custom-box {
  border: 1px solid #ddd;
  padding: 15px;
  border-radius: 5px;
}
</style>

<div class="custom-box">
This text will have a custom border and padding.
</div>

Why Don't My HTML Tags Work on GitHub?

The two most common reasons: first, GitHub has an HTML whitelist — tags like <script>, <iframe>, and <form> are removed directly. Second, GitHub filters out certain CSS properties from the style attribute (like position and z-index that could affect page layout). If you need full control over HTML and CSS, consider using GitHub Pages instead of writing directly in a README.

Can I Still Use the <font> Tag?

Technically yes, but it's not recommended. <font> is deprecated in HTML5, though browsers still support rendering it. The recommended alternative is <span style="color: red;">.

Security Considerations

Security is an important aspect when embedding HTML in Markdown, especially on platforms where users can submit Markdown content.

XSS Risks

Allowing arbitrary HTML in Markdown can create XSS (Cross-Site Scripting) vulnerabilities. For example:

<!-- Dangerous example, do not use in production -->
<img src="x" onerror="alert('XSS')">
<a href="javascript:alert('XSS')">Click me</a>

Most platforms filter out event attributes like onerror and onclick, as well as javascript: protocol links. But if you're building your own Markdown rendering system, make sure to use an XSS filtering library (like DOMPurify) to sanitize HTML content.

Content Security Policy

If you're using a Markdown parsing library (such as marked.js or markdown-it) to build your own application, I recommend:

  1. Always enable HTML sanitization
  2. Use a whitelist rather than a blacklist to filter HTML tags and attributes
  3. Never trust HTML content in user-submitted Markdown

Source: OWASP XSS Prevention Cheat Sheet

Summary

Mixing Markdown and HTML isn't complicated — just remember these key points:

  • Inline HTML tags (<span>, <em>, <a>, etc.) can be used freely in Markdown without affecting Markdown syntax parsing
  • Block-level HTML tags (<div>, <table>, <p>, etc.) do not parse Markdown syntax inside them by default
  • If you need Markdown inside block-level tags, try adding blank lines (CommonMark spec) or the markdown="1" attribute (supported by Python-Markdown / Kramdown)
  • HTML support varies significantly across platforms — always test on your target platform first
  • Security cannot be ignored, especially when accepting user-submitted Markdown content

Ultimately, Markdown and HTML are not opponents. Markdown handles most writing scenarios well enough, and when you occasionally need finer control, HTML fills the gap. Using them together is the most practical approach.

References