WordPress plugin that turns Markdown into Typeform-like forms and pages

blocks.md for WordPress is a plugin that lets you write Markdown, and turns that into Typeform-like forms and web pages. Some great features: unlimited form responses, logic jumps, G-Sheets integration, data-binding, customization and branding, and much more.

Currently v1.0.3
· blocks.md vs. alternatives

Write Markdown on your WordPress pages to create forms and more

Install the plugin and select blocks.md template or blocks.md template RTL as the template for your page. After that, you can just write your Markdown within a preformatted block that has a starting comment of <-- blocks.md -->, and the plugin will take care of the rest. That's all you need to do to create amazing Typeform-like forms and pages on your WordPress site. See video example.

Further reading: WordPress plugin - Docs

·
<!-- blocks.md -->
name* = TextInput( | question = What is your name? | description = Let's get started with the survey. First, please tell us your full legal name according to your passport. ) --- # Hey {$ name $} 馃憢 It's a **pleasure** to meet you. Let us continue. --- email* = EmailInput( | question = What is your email address? | description = Please enter an email address where we can reach you for a reply. Make sure to avoid spelling mistakes. )

Logic jumps and page progress

Add -> {condition} to a slide to conditionally show it, that is, the slide will be shown to the user only if the jump condition is true. This is called a logic jump. Also use |> {percentage|fraction} to show page progress on top. This indicates how much of the form has been completed so far.

Further reading: Slides - Docs

·
<!-- blocks.md -->
specialty* = ChoiceInput( | question = What is your specialty? | description = This will help us with your placement on our team. | choices = Back-end, Front-end, Full-stack ) --- -> specialty == "Back-end" |> 50% At this stage, we're only hiring engineers who have front-end expertise. Sorry for the inconvenience. --- -> specialty == "Front-end" or specialty == "Full-stack" |> 1/2 expertise* = SelectBox( | question = What is your level of expertise? | description = Choose the level of experience you have in the industry. Again, this will help with your placement. | options = Junior, Mid-level, Senior )

Send full and partial responses to your server or Google Sheets

Set the #! post-url = {url} setting to send form responses to that URL using a POST request. There's also an incredibly useful integration that lets you send responses directly to Google Sheets. Responses are sent to the {url} when the user reaches the end slide.

Partial submissions are also supported. Add >> post (case insensitive) to a slide to enable partial submission up to that slide, that is, when a user completes this slide and goes to the next one, all the form data up to that slide will be sent to the URL you set in the setting. See video example.

Further reading: Send responses - Docs

·
<!-- blocks.md -->
#! post-url = {url} >> post phone* = TelInput( | question = What is your phone number? | description = This will allow us to get in touch and fulfill your order. Make sure the number is correct. ) --- products* = PictureChoice( | question = What products would you like? | choices = T-Shirts && https://res.cloudinary.com/dnriuttuy/image/upload/v1720015194/tshirts.png, Socks && https://res.cloudinary.com/dnriuttuy/image/upload/v1720015163/socks.png | multiple )

Collect data of all types

Use different types of inputs to collect all the data you need, ranging from basic types like text, email, and choice inputs to advanced types such as rating, opinion scales (including Net Promoter Score®), etc. More advanced fields are being worked on currently, particularly date time input, file/image upload, payments, etc.

Further reading: Form fields - Docs

·
<!-- blocks.md -->
rating* = RatingInput( | question = How would you rate your experience? | description = Rate your shopping experience so far. This will help us improve our service in the future. ) --- -> rating >= 4 # Thank you! Thanks for rating us so positively. Your support is greatly appreciated by our entire team. --- opinion* = OpinionScale( | question = How likely are you to recommend our product to a friend or colleague? )

Class names and attributes

Add class names and other attributes (such as IDs, WAI-ARIA tags, etc.) to any block-level element by putting them inside [...] and placing this at the very start of the element's syntax. IDs start with #, class names start with ., and other attributes can be added as-is, for example, style="...".

A lot of CSS utility classes are included by default, but you can also easily use Tailwind CSS (or any other similar framework) if you need more control.

Further reading: Class names and attributes - Docs

·
<!-- blocks.md -->
# [#title .anchored] Company profile [style="font-size: 17px;"] Welcome to our company profile where you can find out about our values, offices, and the services we provide. - [.col-6 .xs:col-6 .list-inside] - Mission - Office guide 0. [.col-6 .xs:col-6 .list-inside] 1. London 2. New York [.col-6 .mt-3] email* = EmailInput( | question = Subscribe | subfield | fieldSize = sm )

Data-binding, content division and content span

Create wrapping <div> elements by putting content inside pairs of :::. The content inside can be any valid Markdown, such as headings, paragraphs, lists, form fields, etc. Class names and other attributes are supported via [...] (see above). Moreover, you can bind one or more fields to a <div> element by adding the names of the fields inside {$...$} (separated by spaces), and placing this within the [...]. This means that whenever the value of a binded field changes, the content inside the <div> will be automatically re-rendered.

The templating is done using Nunjucks, so its entire list of features such as if-else statements, loops, filters, etc. are fully supported. You can also bind a single field to a <span> element using {$ field $}. This is obviously not as flexible, but it is simple, and can be used inside paragraphs, headings, list items, etc.

Further reading: Data-binding - Docs

·
<!-- blocks.md -->
product* = SelectBox( | question = Product | options = T-Shirts, Socks | selected = T-Shirts | subfield ) price* = NumberInput( | question = Price | unitEnd = $ | subfield | min = 1 ) quantity* = NumberInput( | question = Quantity | subfield | min = 1 ) [.col-4] Selected: {$ product $} ::: [.col-8 .text-end .xs:text-start {$ price quantity $}] {% if price and quantity -%} Total: ${{ price }} × {{ quantity }} = ${{ price * quantity }} {% else -%} Total: Set price and quantity {% endif %} :::

Highly customizable, including branding

Configure a setting by adding #! {name} = {value}, so you can change things like the color, background-color, accent, etc. You can also add branding, call to action (CTA), change page layout and alignment, and so much more. Moreover, you can remove our branding too using #! blocksmd-branding = hide as soon as you buy a license, so the page becomes entirely your own.

Some settings (like the colors and brand) can accept up to two values in the format #! {name} = {value1} || {value2}. The first value is used for the initial color scheme (which is light by default), and the second value is used for the alternate or dark color scheme. If you change the default color scheme to dark by setting #! color-scheme = dark, then the first value would be used in dark mode, and the second value would be used in light mode.

Further reading: Settings - Docs

·
<!-- blocks.md -->
#! accent = #333333 || #dfdfdf #! accent-foreground = #dfdfdf || #333333 #! background-color = rgb(255, 255, 255) || rgb(26, 26, 26) #! blocksmd-branding = hide #! brand = ![Logo](https://res.cloudinary.com/dnriuttuy/image/upload/v1721059180/logo-lm.svg) || ![Logo](https://res.cloudinary.com/dnriuttuy/image/upload/v1721059180/logo-dm.svg) #! color = #333333 || #dfdfdf #! cta = [Sign up](https://example.com) #! page-progress = hide #! rounded = none plant* = PictureChoice( | question = What type of plant would you like? | choices = House plants && https://res.cloudinary.com/dnriuttuy/image/upload/v1721060655/hplants.jpg, Office plants && https://res.cloudinary.com/dnriuttuy/image/upload/v1721060651/oplants.jpg )

Set and read data, create reactive blog posts and articles

Set local data that can be used in the template by putting valid JSON inside pairs of ``` or ~~~, with the data keyword placed right after the opening pair. You can also read data from a URL using #! get-url = {url}. By default, CSV is the expected format, and the incoming data is formatted so that each key is a spreadsheet cell reference. However, JSON is also supported.

Moreover, you can set #! page = single to create a normal webpage that is not a form. This way, you can create blog posts and articles which are reactive, and change depending on the data. In this example for instance, the data is coming in from this spreadsheet, and the author would only need to turn off cell D3 to show that they are not looking for work. Learn how to connect with Google Sheets.

Further reading: Set and read data - Docs

·
<!-- blocks.md -->
#! get-url = https://docs.google.com/spreadsheets/d/e/2PACX-1vTKJSALN8U91YSqvQZ6bQf24z0okzMM2J9D2VtptW2eASFbIC9dKyj2SlSpeaozczNR-u15mfpHqjuV/pub?gid=0&single=true&output=csv #! page = single ``` data { "name": "John Doe", "email": "john@example.com" } ``` # {{ name }} I'm a {{ A3 }} with {{ C3 }}+ years of experience, with a strong focus on building scalable microservices. I'm proficient in the following languages: - [.list-unstyled] - [x] Python - [x] JavaScript/TypeScript - [x] Go --- {% if D3 -%} I'm currently available for work: {{ email }}. {% else -%} I'm currently not available for work. {% endif %}

Localizable to any language

Set the #! localization = {lang} to a supported language code, and write your Markdown in that language—everything will be automatically translated. Can't find the language of your choice in that list? It is remarkably easy to add support for a new language.

Further reading: Localize - Docs

·
<!-- blocks.md -->
#! localization = es name* = TextInput( | question = C贸mo te llamas? | description = Empecemos con la encuesta. En primer lugar, dinos tu nombre legal completo seg煤n tu pasaporte. ) --- # Hola {$ name $} 馃憢 Es un **placer** conocerte. Continuemos. --- email* = EmailInput( | question = Cu谩l es tu direcci贸n de correo electr贸nico? | description = Introduzca una direcci贸n de correo electr贸nico en la que podamos localizarle para responderle. )

Pricing

Bought already? Get latest version.

Standard WP plugin
1 WordPress site
$59.00
plus local taxes
one-time
  • 1 WordPress site
  • Use for personal or client
  • Unlimited form responses
  • Email support
  • Free updates to any 1.x version
  • Remove blocks.md branding
Multisite WP plugin
Unlimited WordPress sites
$199.00
plus local taxes
one-time
  • Unlimited WordPress sites
  • Use for personal or client
  • Unlimited form responses
  • Email support
  • Free updates to any 1.x version
  • Remove blocks.md branding

If you have any questions or think the prices are too high, send us an email or book a call. We're happy to discuss and provide discounts to people in special circumstances.