Beginner1 hourHTMLCSS

Build a Recipe Web Page with HTML & CSS

A well-structured, good-looking recipe page with an image, ingredients, steps and a facts table.

What you'll build

Live demo: the finished recipe web page. Try it.
<header>
  <p>Sprout Kitchen</p>
</header>
<main>
  <article>
    <h1>Creamy Tomato Pasta</h1>
    <p>A 20-minute weeknight dinner that tastes like it took an hour.</p>
    <img src="https://picsum.photos/id/292/800/450" alt="A bowl of creamy tomato pasta topped with basil" width="800" height="450">
    <table class="facts">
      <tr><th>Prep</th><th>Cook</th><th>Serves</th></tr>
      <tr><td>5 min</td><td>15 min</td><td>2</td></tr>
    </table>
    <h2>Ingredients</h2>
    <ul>
      <li>200g pasta</li>
      <li>1 tin chopped tomatoes</li>
      <li>2 cloves garlic</li>
      <li>100ml cream</li>
      <li>Fresh basil</li>
    </ul>
    <h2>Method</h2>
    <ol>
      <li>Cook the pasta in salted water.</li>
      <li>Fry the garlic for 1 minute, add the tomatoes and simmer.</li>
      <li>Stir in the cream, then the drained pasta.</li>
      <li>Top with basil and serve.</li>
    </ol>
  </article>
</main>
<footer>
  <p>© 2026 Sprout Kitchen</p>
</footer>
ResultEdit the code, then press Run or Ctrl+Enter

Every food blog is built on the same bones: a title, a photo, quick facts, an ingredients list and numbered steps. You'll build one from scratch using semantic HTML (so search engines understand it) and then style it into something you'd actually want to cook from.

Why semantic HTML matters for a recipe

A recipe is one of the most structured kinds of content on the web, and that makes it a perfect place to practise semantic HTML: choosing elements for what they mean, not how they look.

When you wrap the recipe in <article>, put the title in an <h1>, the ingredients in a <ul> and the method in an <ol>, you're telling the browser what each part is. That pays off in three ways:

  • Screen readers can announce "list, 8 items" and let a cook jump straight to the method.
  • Search engines understand the page better, which is why recipe sites are so careful with their markup.
  • Your CSS gets simpler, because you can style article ol li without inventing class names for everything.

The semantic elements lesson covers header, main, article and footer in more detail.

Ordered or unordered list?

Beginners often ask which list to use. The rule is simple: if the order matters, use <ol>. Ingredients can be gathered in any order, so they go in an unordered list. Method steps must happen in sequence, so they go in an ordered list, and the browser numbers them for you. If you later add a step in the middle, the numbers update on their own. Read more in the HTML lists lesson.

The quick facts (prep time, cook time, servings) are a small table, because each fact is a label paired with a value. Tables are for data like this, not for page layout. The HTML tables lesson shows how <th> and <td> work together.

Typography that makes a page easy to read

A recipe gets read in a busy kitchen, often from arm's length, so readable text matters more here than on most pages. Three CSS settings make the biggest difference. A comfortable font-size of at least 1rem (usually 16 pixels) keeps text legible on phones. A line-height of around 1.6 adds breathing room between lines, so eyes don't lose their place. A limited line length, set with max-width, stops paragraphs stretching across a wide screen. Pair a clear sans-serif font for the body with a slightly bolder heading and you have a page that looks considered without any images at all. The CSS fonts lesson and CSS text lesson explain each property.

Add recipe schema so Google understands the page

Search results for recipes often show a photo, a star rating and the cooking time right on the results page. Those rich results come from structured data: a small block of JSON-LD that describes the recipe in a format search engines read. You add it once in the page's <head> or at the end of the <body>:

HTML
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Recipe",
  "name": "Creamy Tomato Soup",
  "image": "soup.jpg",
  "description": "A quick, creamy tomato soup ready in 30 minutes.",
  "prepTime": "PT10M",
  "cookTime": "PT20M",
  "recipeYield": "4 servings",
  "recipeIngredient": ["2 tins of chopped tomatoes", "1 onion", "500 ml vegetable stock"],
  "recipeInstructions": [
    { "@type": "HowToStep", "text": "Soften the onion in a little oil." },
    { "@type": "HowToStep", "text": "Add the tomatoes and stock and simmer for 15 minutes." }
  ]
}
</script>

Times use the ISO 8601 duration format: PT10M means ten minutes and PT1H30M means an hour and a half. The values must match what visitors can see on the page, because search engines ignore markup that describes content that isn't there. Paste your page into Google's Rich Results Test to check it. The SEO meta tags and schema lesson explains structured data in more depth.

Requirements checklist

  • A single <h1> with the recipe name and a short intro paragraph
  • A photo with descriptive alt text
  • A "quick facts" table (prep time, cook time, serves)
  • An unordered list of ingredients and an ordered list of method steps
  • Wrapped in <main> and <article>, with a <header> and <footer>
  • Readable typography and a max width so lines aren't too long

Starter code

Press Run to see where you're starting from, then follow the steps.

<header>
  <p>Sprout Kitchen</p>
</header>
<main>
  <article>
    <h1>Creamy Tomato Pasta</h1>
    <p>A 20-minute weeknight dinner that tastes like it took an hour.</p>
    <!-- add the image, table and lists here -->
  </article>
</main>
<footer>
  <p>© 2026 Sprout Kitchen</p>
</footer>
ResultEdit the code, then press Run or Ctrl+Enter

Step-by-step guide

  1. 1

    Add the photo

    Put the image straight after the intro. Give it real dimensions so the page doesn't jump while loading.

    HTML
    <img src="https://picsum.photos/id/292/800/450"
         alt="A bowl of creamy tomato pasta topped with basil"
         width="800" height="450">
  2. 2

    Add the quick facts table

    Tables are perfect for small sets of labelled data.

    HTML
    <table class="facts">
      <tr><th>Prep</th><th>Cook</th><th>Serves</th></tr>
      <tr><td>5 min</td><td>15 min</td><td>2</td></tr>
    </table>
  3. 3

    Add ingredients and method

    Ingredients don't have an order, so use <ul>. Steps do, so use <ol>.

    HTML
    <h2>Ingredients</h2>
    <ul>
      <li>200g pasta</li>
      <li>1 tin chopped tomatoes</li>
      <li>2 cloves garlic</li>
      <li>100ml cream</li>
      <li>Fresh basil</li>
    </ul>
    
    <h2>Method</h2>
    <ol>
      <li>Cook the pasta in salted water.</li>
      <li>Fry the garlic for 1 minute, add the tomatoes and simmer.</li>
      <li>Stir in the cream, then the drained pasta.</li>
      <li>Top with basil and serve.</li>
    </ol>
  4. 4

    Style the page

    Centre the content, make the text comfortable to read and the image responsive.

    CSS
    body {
      margin: 0;
      font-family: Georgia, serif;
      line-height: 1.7;
      color: #2b2a28;
      background: #fffaf3;
    }
    
    header, footer, main {
      max-width: 680px;
      margin: 0 auto;
      padding: 16px 20px;
    }
    
    h1 { font-size: clamp(2rem, 5vw, 3rem); line-height: 1.1; color: #b4321f; }
    
    article img {
      width: 100%;
      height: auto;
      border-radius: 16px;
    }
  5. 5

    Style the table and lists

    CSS
    .facts {
      width: 100%;
      border-collapse: collapse;
      text-align: center;
      margin: 20px 0;
    }
    
    .facts th { background: #b4321f; color: white; padding: 8px; }
    .facts td { border: 1px solid #ead9c6; padding: 8px; }
    
    ol li::marker { color: #b4321f; font-weight: bold; }
    ul li { margin-bottom: 4px; }

Final solution

Show the complete solution
<header>
  <p>Sprout Kitchen</p>
</header>
<main>
  <article>
    <h1>Creamy Tomato Pasta</h1>
    <p>A 20-minute weeknight dinner that tastes like it took an hour.</p>
    <img src="https://picsum.photos/id/292/800/450" alt="A bowl of creamy tomato pasta topped with basil" width="800" height="450">
    <table class="facts">
      <tr><th>Prep</th><th>Cook</th><th>Serves</th></tr>
      <tr><td>5 min</td><td>15 min</td><td>2</td></tr>
    </table>
    <h2>Ingredients</h2>
    <ul>
      <li>200g pasta</li>
      <li>1 tin chopped tomatoes</li>
      <li>2 cloves garlic</li>
      <li>100ml cream</li>
      <li>Fresh basil</li>
    </ul>
    <h2>Method</h2>
    <ol>
      <li>Cook the pasta in salted water.</li>
      <li>Fry the garlic for 1 minute, add the tomatoes and simmer.</li>
      <li>Stir in the cream, then the drained pasta.</li>
      <li>Top with basil and serve.</li>
    </ol>
  </article>
</main>
<footer>
  <p>© 2026 Sprout Kitchen</p>
</footer>
ResultEdit the code, then press Run or Ctrl+Enter

Bonus challenges

  • Add a "Print recipe" button that calls window.print() and a print stylesheet with @media print.
  • Add a "Jump to method" link using an id and a jump link.
  • Turn the ingredients into checkboxes so cooks can tick them off (input types).

Common mistakes and how to fix them

Using several h1 headings. A page should have one <h1>, the recipe name. Sections such as "Ingredients" and "Method" are <h2>. Skipping levels, like going from h1 straight to h4, makes the outline confusing for assistive technology.

Lines of text that run across the whole screen. Long lines are tiring to read. Give the article a max-width of around 65ch and centre it with margin: 0 auto. The ch unit is roughly the width of one character, so 65ch keeps lines at a comfortable length.

An image that overflows on phones. Add max-width: 100%; height: auto; to images so they shrink with the screen without stretching.

Writing alt text like "image of food". Describe what someone would see: "A bowl of tomato soup topped with basil leaves". If the image adds nothing beyond the text, an empty alt="" is the honest choice.

Check your work

  1. Read the page with the CSS switched off (most browsers let you disable styles in the developer tools). It should still read top to bottom like a proper recipe.
  2. Shrink the browser to phone width. The facts table should still fit, and the photo should resize.
  3. Open the print preview. If you want a cleaner printout, the first bonus challenge shows you how.
  4. Run the page through the W3C validator, which the HTML best practices lesson explains, and fix any warnings it finds.

Make the recipe page printable

Many people still print recipes to cook from. A short print stylesheet hides anything that wastes ink and keeps the important parts together:

CSS
@media print {
  body { background: none; color: #000; font-size: 12pt; }
  img { max-width: 50%; }
  nav, footer, .no-print { display: none; }
  ol li { break-inside: avoid; }
}

break-inside: avoid stops a single method step from being split across two pages. Open the print preview in your browser to test it without wasting paper. Combined with the semantic structure and recipe schema, this gives you a recipe page template you can reuse for every recipe you publish.

Questions beginners ask

Where do I get a food photo? Take your own, or use a photo you have permission to use. Keep the file reasonably small (under 300 KB) so the page loads quickly.

Can I add more recipes? Yes. Copy the file, change the content and link the pages together from a simple index page. That's how many recipe blogs started before content management systems existed.

Why not just use divs? Divs work visually, but they carry no meaning. The page would look the same while being harder for search engines and screen readers to understand. Semantic tags cost nothing extra to type.