diff --git a/tutorials/article.v b/tutorials/article.v
new file mode 100644
index 0000000000..42acf00709
--- /dev/null
+++ b/tutorials/article.v
@@ -0,0 +1,17 @@
+module main
+
+struct Article {
+ id int
+ title string
+ text string
+}
+
+pub fn (app &App) find_all_articles() []Article {
+ db := app.db
+ articles := db.select from Article
+ return articles
+}
+
+
+
+
diff --git a/tutorials/articles1.png b/tutorials/articles1.png
new file mode 100644
index 0000000000..6656c1b1ce
Binary files /dev/null and b/tutorials/articles1.png differ
diff --git a/tutorials/blog.sql b/tutorials/blog.sql
new file mode 100644
index 0000000000..f40fccad10
--- /dev/null
+++ b/tutorials/blog.sql
@@ -0,0 +1,22 @@
+create database blog;
+
+\c blog
+
+drop table articles;
+
+create table articles (
+ id serial primary key,
+ title text default '',
+ text text default ''
+);
+
+insert into articles (title, text) values (
+ 'Hello, world!',
+ 'V is great.'
+);
+
+insert into articles (title, text) values (
+ 'Second post.',
+ 'Hm... what should I write about?'
+);
+
diff --git a/tutorials/blog.v b/tutorials/blog.v
new file mode 100644
index 0000000000..6e0c505524
--- /dev/null
+++ b/tutorials/blog.v
@@ -0,0 +1,48 @@
+module main
+
+import (
+ vweb
+ time
+ pg
+)
+
+struct App {
+mut:
+ vweb vweb.Context
+ db pg.DB
+}
+
+fn main() {
+ app := App{}
+ vweb.run(mut app, 8080)
+}
+
+fn (app mut App) index_text() {
+ app.vweb.text('Hello, world from vweb!')
+}
+
+/*
+fn (app &App) index_html() {
+ message := 'Hello, world from vweb!'
+ $vweb.html()
+}
+*/
+
+fn (app &App) index() {
+ articles := app.find_all_articles()
+ $vweb.html()
+}
+
+pub fn (app mut App) init() {
+ db := pg.connect(pg.Config{
+ host: '127.0.0.1'
+ dbname: 'blog'
+ user: 'alex'
+ }) or { panic(err) }
+ app.db = db
+}
+
+fn (app mut App) time() {
+ app.vweb.text(time.now().format())
+}
+
diff --git a/tutorials/building-a-simple-web-blog-with-vweb.md b/tutorials/building-a-simple-web-blog-with-vweb.md
new file mode 100644
index 0000000000..50a19cdf22
--- /dev/null
+++ b/tutorials/building-a-simple-web-blog-with-vweb.md
@@ -0,0 +1,315 @@
+Hello,
+
+In this guide we'll build a simple web blog in V.
+
+The benefits of using V for web:
+- A safe, fast, language with the development speed of Python and
+the performance of C.
+- Zero dependencies: everything you need for web development comes with the language
+in a 1 MB package.
+- Very small resulting binaries: the blog we'll create in this tutorial is about 150 KB.
+- Easy deployments: a single binary file that even includes the precompiled templates.
+- Runs on the cheapest hardare with minimum footprint: for most apps a $3 instance
+is enough.
+
+*Please note that V and Vweb are at a very early stage and are changing rapidly.*
+
+
+### Installing V
+
+```
+wget https://github.com/vlang/v/releases/latest/download/v_linux.zip
+unzip v_linux.zip
+cd v
+sudo ./v symlink
+```
+
+Now V should be globally available on your system.
+
+> On macOS use `v_macos.zip`, on Windows - `v_windows.zip`.
+If you use a BSD system, Solaris, Android, or simply want to install V
+from source, follow the simple instructions here:
+https://github.com/vlang/v#installing-v-from-source
+
+
+### Creating a new Vweb project
+
+V projects can be created anywhere and don't need to have any structure:
+
+```bash
+mkdir blog
+cd blog
+touch blog.v
+```
+
+First let's create a simple hello world website:
+
+```v
+// blog.v
+module main
+
+import (
+ vweb
+)
+
+struct App {
+ vweb vweb.Context
+}
+
+fn main() {
+ app := App{}
+ vweb.run(mut app, 8080)
+}
+
+fn (app mut App) index() {
+ app.vweb.text('Hello, world from vweb!')
+}
+
+pub fn (app &App) init() {}
+
+```
+
+Run it with
+
+```bash
+v run blog.v
+```
+
+and open http://localhost:8080/ in your browser:
+
+
+
+The `App` struct is an entry point of our web application. If you have experience
+with an MVC web framework, you can think of it as a controller. (Vweb is
+not an MVC framework however.)
+
+As you can see, there are no routing rules. The `index()` action handles the `/` request by default.
+Vweb often uses convention over configuration, and adding a new action requires
+no routing rules either:
+
+```v
+fn (app mut App) time() {
+ app.vweb.text(time.now().format())
+}
+```
+
+
+
+
+>You have to rebuild and restart the website every time you change the code.
+In the future Vweb will detect changes and recompile the website in the background
+while it's running.
+
+The `.text(string)` method obviously returns a plain text document with the provided
+text, which isn't frequently used in websites.
+
+
+### HTML View
+
+
+Let's return an HTML view instead. Create `index.html` in the same directory:
+
+```html
+
+
+
+ @message
+
+
+
+
+```
+
+and update our `index()` action so that it returns the HTML view we just created:
+
+```v
+fn (app mut App) index() {
+ message := 'Hello, world from Vweb!'
+ $vweb.html()
+}
+```
+
+
+
+Good, now we have an actual HTML page.
+
+The V template language is similar to C#'s Razor: `@message` prints the value
+of `message`.
+
+You may notice something unusual: the `message` variable created in the `index()`
+action is automatically available in the view.
+
+It's another feature of Vweb to reduce the boilerplate in your web apps.
+No need to create view models just to pass data, or use an unsafe and untyped
+alternative, like C#'s `ViewBag["message"]`.
+
+Making all action's variables available in the view may seem crazy,
+but V is a language with pure functions by default, and you won't be able
+to modify any data from a view. `@foo.bar()` will only work if the `bar()` method
+doesn't modify `foo`.
+
+The HTML template is compiled to V during the compilation of the website, that's done by the `$vweb.html()` line.
+(`$` always means compile time actions in V.) offering the following benefits:
+
+- Great performance, since the templates don't need to be compiled
+on every request, like in almost every major web framework.
+
+- Easier deployment, since all your HTML templates are compiled
+into a single binary file together with the web application itself.
+
+- All errors in the templates are guaranteed to be caught during compilation.
+
+### Fetching data with V ORM
+
+Now let's display some articles!
+
+We'll be using V's builtin ORM and a Postgres database. (V ORM will also
+support MySQL, SQLite, and SQL Server in the near future.)
+
+Create a SQL file with the schema:
+```sql
+create database blog;
+
+\c blog
+
+drop table articles;
+
+create table articles (
+ id serial primary key,
+ title text default '',
+ text text default ''
+);
+
+insert into articles (title, text) values (
+ 'Hello, world!',
+ 'V is great.'
+);
+
+insert into articles (title, text) values (
+ 'Second post.',
+ 'Hm... what should I write about?'
+);
+```
+
+Run the file with `psql -f blog.sql`.
+
+
+Add a Postgres DB handle to `App`:
+
+```v
+struct App {
+mut:
+ vweb vweb.Context
+ db pg.DB
+}
+```
+
+
+
+Modify the `init()` method we created earlier to connect to a database:
+
+```v
+pub fn (app mut App) init() {
+ db := pg.connect(pg.Config{
+ host: '127.0.0.1'
+ dbname: 'blog'
+ user: 'blog'
+ }) or { panic(err) }
+ app.db = db
+}
+```
+
+Code in the `init()` function is run only once during app's startup, so we are going
+to have one DB connection for all requests.
+
+Create a new file `article.v`:
+
+
+```v
+
+module main
+
+struct Article {
+ id int
+ title string
+ text string
+}
+
+pub fn (app & App) find_all_articles() []Article {
+ db := app.db
+ articles := db.select from Article
+ return articles
+}
+```
+
+Let's fetch the articles in the `index()` action:
+
+```v
+fn (app &App) index() {
+ articles := app.find_all_articles()
+ $vweb.html()
+}
+```
+
+
+Finally, let's update our view:
+
+```html
+
+ @for article in articles
+
+ @article.title
+ @article.text
+
+ @end
+
+```
+
+```bash
+v run .
+```
+
+
+
+That was very simple, wasn't it?
+
+The built-in V ORM uses a syntax very similar to SQL. The queries are built with V.
+For example, if we only wanted to find articles with ids between 100 and 200, we'd do:
+
+```
+articles := db.select from Article where id >= 100 && id <= 200
+```
+
+Retrieving a single article is very simple:
+
+```v
+
+pub fn (app &App) retrieve_article() ?Article {
+ db := app.db
+ article := db.select from Article limit 1
+ return article
+}
+```
+
+V ORM uses V's optionals for single values, which is very useful, since
+bad queries will always be handled by the developer:
+
+```v
+article := app.retrieve_article(10) or {
+ app.vweb.text('Article not found')
+ return
+}
+```
+
+
+> Temporary variables like `db := app.db` are a temporary limitation in the
+V ORM, soon they will not be needed.
+
+To be continued on Dec 14...
+
+
+
+
+
diff --git a/tutorials/hello.png b/tutorials/hello.png
new file mode 100644
index 0000000000..f44a41e11e
Binary files /dev/null and b/tutorials/hello.png differ
diff --git a/tutorials/hello_html.png b/tutorials/hello_html.png
new file mode 100644
index 0000000000..a5800d8018
Binary files /dev/null and b/tutorials/hello_html.png differ
diff --git a/tutorials/index.html b/tutorials/index.html
new file mode 100644
index 0000000000..4ec36c56a6
--- /dev/null
+++ b/tutorials/index.html
@@ -0,0 +1,14 @@
+
+
+
+ @for article in articles
+
+ @article.title
+ @article.text
+
+ @end
+
+
+
diff --git a/tutorials/time.png b/tutorials/time.png
new file mode 100644
index 0000000000..b0f8128f17
Binary files /dev/null and b/tutorials/time.png differ