Your ERP Already Has a Website Builder. You Just Never Turned It On.
Most Odoo customers pay for a separate WordPress or Wix subscription to run their public website. They maintain two systems, two login credentials, two sets of analytics, and a fragile API bridge to sync products and contact forms back to the ERP. When the bridge breaks (and it always breaks), leads fall through the cracks and product pages show stale inventory counts.
Odoo 19's Website module eliminates that gap entirely. It ships a full drag-and-drop website builder that reads directly from the same database your Sales, Inventory, and Accounting modules use. Product pages pull live stock levels. Contact forms create leads in CRM instantly. Blog posts are indexed alongside your knowledge base. And the entire editing experience happens in a visual editor that requires zero HTML, CSS, or JavaScript knowledge.
This guide walks through the complete setup: installing the website module, choosing and customizing a theme, building pages with the drag-and-drop editor, configuring menus and forms, integrating the blog, enabling multi-language support, and using the built-in SEO tools to rank on Google. Every step uses the standard Odoo 19 UI with no custom code.
Installing the Website Module and Choosing a Theme in Odoo 19
The Website module is not installed by default on new Odoo 19 databases. You need to activate it explicitly, choose a theme, and configure the basic site identity before you can start building pages.
Step 1: Install the Website Module
Navigate to Apps, search for "Website," and click Install. This pulls in the core website module plus its dependencies: portal, web_editor, http_routing, and web_unsplash (free stock photos from Unsplash built right into the editor).
# Install website module via CLI
./odoo-bin -c /etc/odoo/odoo.conf \
-d your_database \
-i website \
--stop-after-init
# Verify installation
./odoo-bin shell -c /etc/odoo/odoo.conf -d your_database <<EOF
modules = env['ir.module.module'].search([
('name', 'in', ['website', 'web_editor', 'portal']),
('state', '=', 'installed')
])
for m in modules:
print(f"{{m.name}} - {{m.state}} (v{{m.installed_version}})")
EOFStep 2: Select a Theme
After installation, Odoo redirects you to the theme picker. Odoo 19 ships with 12 built-in themes organized by industry. Each theme is an installable module that overrides CSS variables, default building blocks, and page layouts. You can switch themes later, but it will reset page-level style overrides.
| Theme | Best For | Key Features |
|---|---|---|
| Default | Clean, minimal sites | Neutral typography, highly customizable, lightest CSS footprint |
| Starter | B2B companies, consultancies | Professional header, feature grids, testimonial blocks |
| Graphene | Tech companies, SaaS | Dark mode support, code-friendly typography, gradient sections |
| Nano | E-commerce, retail | Product-focused layouts, large hero images, cart-optimized navigation |
| Aviato | Travel, hospitality | Full-width imagery, booking-oriented CTAs, parallax scrolling |
Step 3: Configure Site Identity
Go to Website → Configuration → Settings. Set your company name, logo, favicon, default language, and social media links. These values propagate to the header, footer, and structured data across every page.
# Set website identity via shell
website = env['website'].browse(1)
website.write({
'name': 'My Company Website',
'domain': 'https://www.mycompany.com',
'social_facebook': 'https://facebook.com/mycompany',
'social_linkedin': 'https://linkedin.com/company/mycompany',
'social_twitter': 'https://x.com/mycompany',
'google_analytics_key': 'G-XXXXXXXXXX',
'plausible_shared_key': '', # Alternative to GA
})
env.cr.commit() In Website → Configuration → Settings → Domain, enter your custom domain (e.g., www.mycompany.com). Odoo uses this for canonical URLs, sitemap generation, and social sharing previews. If you run multiple websites (multi-website mode), each site gets its own domain. DNS must point to your Odoo server, and Nginx should terminate SSL before proxying to Odoo.
Using the Odoo 19 Drag-and-Drop Editor: Building Blocks, Columns, and Styling
The website editor in Odoo 19 is a WYSIWYG page builder that runs directly in the browser. Click Edit on any page to enter edit mode. The left panel shows a library of pre-built building blocks you can drag onto the page canvas. Every block is fully configurable: colors, spacing, animations, visibility rules, and responsive behavior.
Building Block Categories
| Category | Block Count | Examples |
|---|---|---|
| Structure | 18 | Banner, Cover, Text-Image, Columns, Separator, Masonry Grid |
| Features | 14 | Steps, Comparisons, Accordion FAQ, Tabs, Timeline, Counters |
| Dynamic Content | 8 | Blog Posts, Products, Events, Team Members, Testimonials, Pricing |
| Inner Content | 12 | Buttons, Badges, Cards, Alerts, Progress Bars, Social Icons |
Step 1: Add a Hero Section
Drag a Cover block onto the top of your page. Click the background to set an image (upload your own or pick from the built-in Unsplash integration). Double-click the text to edit the headline and subtitle. Use the toolbar to adjust font size, weight, and color.
Step 2: Build a Feature Grid
Drag a Columns block below the hero. Select 3 or 4 columns. Each column can contain an icon, heading, and description. Click any element to customize. The Style tab on the right panel lets you set padding, margin, background color, border radius, and shadow for any element.
Step 3: Theme Customization via the Style Panel
Click the paintbrush icon in the top toolbar to open the global Theme Customizer. This panel controls site-wide variables without touching CSS:
// These variables are set via the visual UI
// Stored in website.theme_customize_asset
$o-color-1: #1B3A4B; // Primary brand color
$o-color-2: #065A82; // Secondary color
$o-color-3: #1C7293; // Accent color
$o-color-4: #F4F4F4; // Light background
$o-color-5: #FFFFFF; // White
$font-family-base: 'Inter', sans-serif;
$headings-font-family: 'Plus Jakarta Sans', sans-serif;
$font-size-base: 1rem; // 16px
$h1-font-size: 3.25rem; // 52px
$btn-border-radius: 0.5rem;
$navbar-height: 72px;
// Button styles
$btn-primary-color: $o-color-1;
$btn-primary-bg: $o-color-3;
$btn-primary-border-color: $o-color-3;Every change you make in the Theme Customizer updates these SCSS variables. Odoo recompiles the CSS on save. This means your color and font choices cascade automatically across the header, footer, buttons, links, and all building blocks on every page.
In edit mode, use the device icons in the top toolbar to preview your page on desktop, tablet, and mobile viewports. Odoo 19 building blocks are responsive by default, but you can set per-device visibility on any element: right-click a block, select Visibility, and choose which breakpoints it appears on. This lets you show a compact mobile layout while keeping a full-width desktop version.
Building Contact Forms and Integrating the Blog in Odoo 19 Website
Contact Forms That Create CRM Leads
The Form Builder block in the website editor creates forms that write directly to Odoo models. No Zapier, no webhooks, no middleware. Drag a Form block onto any page, and configure where submissions go:
| Action | Target Model | What Happens on Submit |
|---|---|---|
| Create an Opportunity | crm.lead | New lead in your CRM pipeline with all form fields mapped to lead fields |
| Create a Ticket | helpdesk.ticket | New support ticket assigned to the default helpdesk team |
| Subscribe to Newsletter | mailing.contact | Adds the email to a mailing list for email marketing campaigns |
| Create a Task | project.task | New task in a specified project (useful for internal request forms) |
| Send an Email | N/A | Sends form data as an email to specified recipients (no record created) |
Step 1: Build a Lead Capture Form
Drag a Form block onto your Contact page. In the form settings (click the form, then the settings icon), select "Create an Opportunity" as the action. Add fields by clicking + Add a field. Map each form field to a CRM lead field:
<section class="s_website_form" data-snippet="s_website_form">
<form action="/website/form/"
method="post"
data-model_name="crm.lead"
data-success_mode="redirect"
data-success_page="/thank-you">
<!-- Hidden field: assign to Sales Team -->
<input type="hidden"
name="team_id"
value="1"/>
<!-- Visible fields -->
<div class="s_website_form_field">
<label for="contact_name">Your Name</label>
<input type="text"
name="contact_name"
required="required"/>
</div>
<div class="s_website_form_field">
<label for="email_from">Email</label>
<input type="email"
name="email_from"
required="required"/>
</div>
<div class="s_website_form_field">
<label for="phone">Phone</label>
<input type="tel"
name="phone"/>
</div>
<div class="s_website_form_field">
<label for="description">Message</label>
<textarea name="description"
rows="4"></textarea>
</div>
<button type="submit" class="btn btn-primary">
Send Message
</button>
</form>
</section>Go to Website → Configuration → Settings → Privacy and enter your Google reCAPTCHA v3 Site Key and Secret Key. Once configured, every website form automatically includes invisible reCAPTCHA validation. Without this, your CRM will fill up with spam leads within days of going live.
Blog Integration
Install the Website Blog module (website_blog) from the Apps menu. This adds a /blog section to your site with:
- Multiple blogs: Create separate blogs for News, Product Updates, Industry Insights, etc.
- Rich editor: Write posts using the same drag-and-drop editor as pages. Add code blocks, image galleries, embedded videos, and call-to-action building blocks.
- Tags and categories: Organize posts with tags. Visitors can filter by tag. Tags are included in structured data for Google.
- Author profiles: Each post shows the author with avatar, bio, and link to their other posts.
- Cover images: Used for blog listing cards, social sharing previews, and RSS feed.
- Comments: Optional. Enable per-blog via settings. Comments are moderated by default.
# Install blog module
./odoo-bin -c /etc/odoo/odoo.conf \
-d your_database \
-i website_blog \
--stop-after-init
# Create a blog post via shell
./odoo-bin shell -c /etc/odoo/odoo.conf -d your_database <<EOF
blog = env['blog.blog'].search([], limit=1)
post = env['blog.post'].create({
'name': 'Welcome to Our New Website',
'blog_id': blog.id,
'subtitle': 'Built with Odoo 19 Website Builder',
'content': '<p>We are excited to launch...</p>',
'tag_ids': [(0, 0, {'name': 'Company News'})],
'website_published': True,
})
print(f"Created: {{post.name}} at /blog/{{post.blog_id.name}}/{{post.id}}")
env.cr.commit()
EOFDynamic Blog Blocks on Other Pages
You can embed live blog post cards on any page. In the editor, drag the Blog Posts dynamic block onto your homepage. Configure it to show the 3 latest posts, filter by tag, or pin specific posts. The block updates automatically when new posts are published.
Setting Up Multi-Language Websites in Odoo 19
Odoo 19 handles multi-language at the framework level, not as an afterthought plugin. Every text field in the database supports translations. The website module extends this to pages, menus, building blocks, blog posts, and SEO metadata.
Step 1: Activate Languages
Go to Settings → General Settings → Languages. Click "Add a Language" and select from the 50+ languages Odoo ships with. Each activated language gets a URL prefix: /en, /fr, /es, /ar.
# Activate French and Arabic
for lang_code in ['fr_FR', 'ar_001']:
lang = env['res.lang']._activate_lang(lang_code)
print(f"Activated: {{lang.name}} ({{lang.code}})")
# Set website languages
website = env['website'].browse(1)
langs = env['res.lang'].search([
('code', 'in', ['en_US', 'fr_FR', 'ar_001'])
])
website.language_ids = langs
website.default_lang_id = env.ref('base.lang_en')
env.cr.commit()Step 2: Translate Website Content
Switch to the target language using the language selector in the website header. Enter edit mode. Every text element shows the original text with a translation indicator. Click the text and type the translation directly in the editor. Odoo stores the translation in ir.translation linked to the original element.
Step 3: SEO Metadata Per Language
Each language version of a page has its own meta title, meta description, and Open Graph data. Switch to the target language, open the SEO panel (Promote tab), and enter localized metadata. Odoo 19 automatically generates hreflang tags linking all language versions:
<!-- Odoo 19 generates these automatically -->
<link rel="alternate" hreflang="en"
href="https://www.mycompany.com/en/services"/>
<link rel="alternate" hreflang="fr"
href="https://www.mycompany.com/fr/services"/>
<link rel="alternate" hreflang="ar"
href="https://www.mycompany.com/ar/services"/>
<link rel="alternate" hreflang="x-default"
href="https://www.mycompany.com/en/services"/> Odoo 19 automatically switches to RTL layout for Arabic, Hebrew, and other RTL languages. The CSS direction: rtl is applied at the <html> level, and building blocks mirror their flex/grid layout. You do not need to create separate pages. However, test your custom CSS overrides in RTL mode because hardcoded margin-left or padding-right values do not auto-flip.
Odoo 19 Built-In SEO Tools: Meta Tags, Sitemap, Structured Data, and URL Management
Odoo 19 ships with a comprehensive SEO toolkit built into the website editor. It covers the basics most businesses need without requiring Yoast, RankMath, or any third-party SEO plugin.
The SEO Panel (Promote Tab)
While editing any page, click the "Promote" tab in the top toolbar. This opens the SEO panel where you configure:
- Meta Title: Custom title tag (defaults to page name + site name). Keep it under 60 characters.
- Meta Description: Custom description shown in search results. Keep it under 155 characters.
- URL Slug: Edit the page URL directly. Odoo creates a 301 redirect from the old URL automatically.
- Keyword Analysis: Enter a target keyword and Odoo scans the page for keyword density in the title, headings, body text, image alt tags, and URL.
- Social Preview: Preview how the page will look when shared on Google, Facebook, and Twitter/X.
Automatic Sitemap
Odoo 19 auto-generates a /sitemap.xml that includes all published pages, blog posts, product pages, and event pages. It updates dynamically as you publish or unpublish content. Submit this to Google Search Console.
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<url>
<loc>https://www.mycompany.com/services</loc>
<lastmod>2026-03-10</lastmod>
<priority>0.8</priority>
<!-- Multi-language alternates -->
<xhtml:link rel="alternate" hreflang="en"
href="https://www.mycompany.com/en/services"/>
<xhtml:link rel="alternate" hreflang="fr"
href="https://www.mycompany.com/fr/services"/>
</url>
<url>
<loc>https://www.mycompany.com/blog/news/welcome-post-1</loc>
<lastmod>2026-03-12</lastmod>
<priority>0.6</priority>
</url>
</urlset>Structured Data (JSON-LD)
Odoo 19 automatically injects JSON-LD structured data for key page types:
| Page Type | Schema Type | Included Fields |
|---|---|---|
| Homepage | Organization | Name, logo, URL, social profiles, contact info |
| Product page | Product | Name, price, currency, availability, images, ratings |
| Blog post | Article | Headline, author, date, image, description |
| Event | Event | Name, date, location, ticket price, organizer |
| All pages | BreadcrumbList | Hierarchical breadcrumb path |
URL Redirects
Go to Website → Configuration → Redirects to manage URL redirects. You can create 301 (permanent), 302 (temporary), or 404 (gone) redirects. Odoo auto-creates 301 redirects when you change a page's URL slug via the SEO panel. This prevents broken links after restructuring your site.
# Redirect old WordPress URLs to new Odoo pages
redirects = [
('/about-us/', '/about'),
('/services/erp/', '/services/odoo-implementation'),
('/blog/2025/01/01/', '/blog/news/old-post-1'),
('/contact-us.html', '/contact-us'),
]
for old_url, new_url in redirects:
env['website.rewrite'].create({
'name': f'Redirect {{old_url}}',
'url_from': old_url,
'url_to': new_url,
'redirect_type': '301',
'website_id': 1,
})
print(f"301: {{old_url}} -> {{new_url}}")
env.cr.commit() Odoo 19 does not have a native Search Console API integration. However, you can verify site ownership by adding the Google verification meta tag in Website → Configuration → Settings → SEO. For ongoing monitoring, submit your /sitemap.xml URL and check the Coverage report weekly for crawl errors, especially after publishing new pages or changing URL slugs.
5 Odoo 19 Website Builder Mistakes That Kill Your SEO and User Experience
Forgetting to Set Image Alt Text on Every Building Block
The drag-and-drop editor makes it effortless to add images, but the alt text field defaults to the filename (e.g., IMG_20260310_143022.jpg). Google uses alt text to understand image content, and screen readers depend on it for accessibility. A site with 50 images and no alt text is penalized in both Core Web Vitals accessibility audits and image search rankings.
Click every image in edit mode and set a descriptive alt text in the image options panel. Include your target keyword where natural. For decorative images (backgrounds, dividers), set alt to empty (alt="") so screen readers skip them. Audit with Lighthouse before publishing.
Publishing Pages Without Configuring the Meta Description
If you skip the Promote tab and publish directly, Odoo uses the first ~155 characters of visible body text as the meta description. For pages that start with a building block like a counter or icon grid, this produces gibberish in search results: "24/7 99.9% 500+ Contact us today for a free..." This tanks your click-through rate.
Make the Promote tab part of your publishing checklist. Before toggling a page live, set a custom meta title and meta description that includes your target keyword and a clear call-to-action. Use the Social Preview section to verify how the page will appear in Google and on social media.
Using Too Many Building Blocks and Bloating Page Load Time
Each building block loads its own CSS and JavaScript snippets. A page with 30+ blocks can generate 200+ DOM elements and 500KB+ of CSS. Core Web Vitals' Largest Contentful Paint (LCP) score drops below the "Good" threshold, and Google will rank the page lower on mobile results. We have seen Odoo websites with 4-second LCP scores simply because the homepage had 40 building blocks.
Limit your homepage to 8-12 building blocks. Use the browser DevTools Network tab to measure total transferred size. Enable Nginx gzip compression (see our Nginx reverse proxy guide) and set aggressive cache headers on static assets. Target LCP under 2.5 seconds.
Not Enabling CDN for Static Assets
By default, Odoo serves images, CSS, and JavaScript from the same origin server. If your audience is global, users in Asia hitting a European server experience 300-500ms of additional latency per asset. With 50 assets per page load, that adds up to seconds of unnecessary wait time.
Go to Website → Configuration → Settings → CDN. Enter your CDN base URL (e.g., https://cdn.mycompany.com). Odoo rewrites all static asset URLs to point to the CDN. Cloudflare, AWS CloudFront, and BunnyCDN all work. Configure the CDN to pull from your origin on cache miss.
Switching Themes After Building Pages
Themes in Odoo are installable modules that override SCSS variables and default building block styles. When you switch themes on a site with existing pages, per-block style overrides can conflict with the new theme's defaults. Background colors, font sizes, and spacing values you set manually may render differently. In the worst case, custom CSS classes from the old theme produce unstyled blocks.
Choose your theme before building pages, not after. If you must switch, do it on a staging copy first and review every page. Pay special attention to pages with custom colors and spacing. The Default theme is the safest starting point because it has the smallest CSS footprint and the fewest opinionated overrides.
What Consolidating Your Website into Odoo Saves Your Business
The ROI of using Odoo's website builder is not just about saving on a WordPress license. It is about eliminating the integration layer between your public website and your business operations:
No Zapier, no webhook middleware, no API syncs. Forms create CRM leads instantly. Product pages show live inventory. The website and ERP share one database.
Every form submission writes directly to crm.lead. No middleware failures, no lost submissions, no "the Zapier zap stopped running 3 weeks ago" surprises.
Marketing edits the website, Sales views the leads, Support sees the tickets. All in one system with one set of user permissions. No context-switching between tools.
Beyond cost savings, there is a speed advantage. When your marketing team can publish a new landing page, attach a lead capture form, and see the resulting CRM leads in the same system — all within 30 minutes — your campaign cycle time drops from weeks to hours. That speed compounds over every product launch, event, and seasonal promotion.
Optimization Metadata
Complete guide to the Odoo 19 Website Builder. Drag-and-drop editor, theme customization, forms, blog, multi-language, and SEO tools. No code required.
1. "Installing the Website Module and Choosing a Theme in Odoo 19"
2. "Using the Odoo 19 Drag-and-Drop Editor: Building Blocks, Columns, and Styling"
3. "Building Contact Forms and Integrating the Blog in Odoo 19 Website"
4. "Odoo 19 Built-In SEO Tools: Meta Tags, Sitemap, Structured Data, and URL Management"
5. "5 Odoo 19 Website Builder Mistakes That Kill Your SEO and User Experience"