diff --git a/library/src/main/java/ru/noties/markwon/SpannableConfiguration.java b/library/src/main/java/ru/noties/markwon/SpannableConfiguration.java
index 3aa18cb1..73c8e57b 100644
--- a/library/src/main/java/ru/noties/markwon/SpannableConfiguration.java
+++ b/library/src/main/java/ru/noties/markwon/SpannableConfiguration.java
@@ -31,6 +31,7 @@ public class SpannableConfiguration {
     private final UrlProcessor urlProcessor;
     private final SpannableHtmlParser htmlParser;
     private final ImageSizeResolver imageSizeResolver;
+    private final SpannableFactory spannableFactory; // @since 1.1.0
 
     private SpannableConfiguration(@NonNull Builder builder) {
         this.theme = builder.theme;
@@ -40,6 +41,7 @@ public class SpannableConfiguration {
         this.urlProcessor = builder.urlProcessor;
         this.htmlParser = builder.htmlParser;
         this.imageSizeResolver = builder.imageSizeResolver;
+        this.spannableFactory = builder.spannableFactory;
     }
 
     @NonNull
@@ -77,6 +79,11 @@ public class SpannableConfiguration {
         return imageSizeResolver;
     }
 
+    @NonNull
+    public SpannableFactory factory() {
+        return spannableFactory;
+    }
+
     @SuppressWarnings("unused")
     public static class Builder {
 
@@ -88,6 +95,7 @@ public class SpannableConfiguration {
         private UrlProcessor urlProcessor;
         private SpannableHtmlParser htmlParser;
         private ImageSizeResolver imageSizeResolver;
+        private SpannableFactory spannableFactory;
 
         Builder(@NonNull Context context) {
             this.context = context;
@@ -138,6 +146,15 @@ public class SpannableConfiguration {
             return this;
         }
 
+        /**
+         * @since 1.1.0
+         */
+        @NonNull
+        public Builder spannableFactory(@NonNull SpannableFactory spannableFactory) {
+            this.spannableFactory = spannableFactory;
+            return this;
+        }
+
         @NonNull
         public SpannableConfiguration build() {
 
@@ -165,6 +182,11 @@ public class SpannableConfiguration {
                 imageSizeResolver = new ImageSizeResolverDef();
             }
 
+            // @since 1.1.0
+            if (spannableFactory == null) {
+                spannableFactory = SpannableFactoryDef.create();
+            }
+
             if (htmlParser == null) {
                 htmlParser = SpannableHtmlParser.create(theme, asyncDrawableLoader, urlProcessor, linkResolver, imageSizeResolver);
             }
diff --git a/library/src/main/java/ru/noties/markwon/SpannableFactory.java b/library/src/main/java/ru/noties/markwon/SpannableFactory.java
new file mode 100644
index 00000000..a9dbcd04
--- /dev/null
+++ b/library/src/main/java/ru/noties/markwon/SpannableFactory.java
@@ -0,0 +1,73 @@
+package ru.noties.markwon;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import java.util.List;
+
+import ru.noties.markwon.renderer.ImageSize;
+import ru.noties.markwon.renderer.ImageSizeResolver;
+import ru.noties.markwon.spans.AsyncDrawable;
+import ru.noties.markwon.spans.LinkSpan;
+import ru.noties.markwon.spans.SpannableTheme;
+import ru.noties.markwon.spans.TableRowSpan;
+
+/**
+ * Each method can return null or a Span object or an array of spans
+ *
+ * @since 1.1.0
+ */
+public interface SpannableFactory {
+
+    @Nullable
+    Object strongEmphasis();
+
+    @Nullable
+    Object emphasis();
+
+    @Nullable
+    Object blockQuote(@NonNull SpannableTheme theme);
+
+    @Nullable
+    Object code(@NonNull SpannableTheme theme, boolean multiline);
+
+    @Nullable
+    Object orderedListItem(@NonNull SpannableTheme theme, int startNumber);
+
+    @Nullable
+    Object bulletListItem(@NonNull SpannableTheme theme, int level);
+
+    @Nullable
+    Object thematicBreak(@NonNull SpannableTheme theme);
+
+    @Nullable
+    Object heading(@NonNull SpannableTheme theme, int level);
+
+    @Nullable
+    Object strikethrough();
+
+    @Nullable
+    Object taskListItem(@NonNull SpannableTheme theme, int blockIndent, boolean isDone);
+
+    @Nullable
+    Object tableRow(
+            @NonNull SpannableTheme theme,
+            @NonNull List<TableRowSpan.Cell> cells,
+            boolean isHeader,
+            boolean isOdd);
+
+    @Nullable
+    Object image(
+            @NonNull SpannableTheme theme,
+            @NonNull String destination,
+            @NonNull AsyncDrawable.Loader loader,
+            @NonNull ImageSizeResolver imageSizeResolver,
+            @Nullable ImageSize imageSize,
+            boolean replacementTextIsLink);
+
+    @Nullable
+    Object link(
+            @NonNull SpannableTheme theme,
+            @NonNull String destination,
+            @NonNull LinkSpan.Resolver resolver);
+}
diff --git a/library/src/main/java/ru/noties/markwon/SpannableFactoryDef.java b/library/src/main/java/ru/noties/markwon/SpannableFactoryDef.java
new file mode 100644
index 00000000..2ff060e3
--- /dev/null
+++ b/library/src/main/java/ru/noties/markwon/SpannableFactoryDef.java
@@ -0,0 +1,121 @@
+package ru.noties.markwon;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.style.StrikethroughSpan;
+
+import java.util.List;
+
+import ru.noties.markwon.renderer.ImageSize;
+import ru.noties.markwon.renderer.ImageSizeResolver;
+import ru.noties.markwon.spans.AsyncDrawable;
+import ru.noties.markwon.spans.AsyncDrawableSpan;
+import ru.noties.markwon.spans.BlockQuoteSpan;
+import ru.noties.markwon.spans.BulletListItemSpan;
+import ru.noties.markwon.spans.CodeSpan;
+import ru.noties.markwon.spans.EmphasisSpan;
+import ru.noties.markwon.spans.HeadingSpan;
+import ru.noties.markwon.spans.LinkSpan;
+import ru.noties.markwon.spans.OrderedListItemSpan;
+import ru.noties.markwon.spans.SpannableTheme;
+import ru.noties.markwon.spans.StrongEmphasisSpan;
+import ru.noties.markwon.spans.TableRowSpan;
+import ru.noties.markwon.spans.TaskListSpan;
+import ru.noties.markwon.spans.ThematicBreakSpan;
+
+public class SpannableFactoryDef implements SpannableFactory {
+
+    @NonNull
+    public static SpannableFactoryDef create() {
+        return new SpannableFactoryDef();
+    }
+
+    @Nullable
+    @Override
+    public Object strongEmphasis() {
+        return new StrongEmphasisSpan();
+    }
+
+    @Nullable
+    @Override
+    public Object emphasis() {
+        return new EmphasisSpan();
+    }
+
+    @Nullable
+    @Override
+    public Object blockQuote(@NonNull SpannableTheme theme) {
+        return new BlockQuoteSpan(theme);
+    }
+
+    @Nullable
+    @Override
+    public Object code(@NonNull SpannableTheme theme, boolean multiline) {
+        return new CodeSpan(theme, multiline);
+    }
+
+    @Nullable
+    @Override
+    public Object orderedListItem(@NonNull SpannableTheme theme, int startNumber) {
+        // todo| in order to provide real RTL experience there must be a way to provide this string
+        return new OrderedListItemSpan(theme, String.valueOf(startNumber) + "." + '\u00a0');
+    }
+
+    @Nullable
+    @Override
+    public Object bulletListItem(@NonNull SpannableTheme theme, int level) {
+        return new BulletListItemSpan(theme, level);
+    }
+
+    @Nullable
+    @Override
+    public Object thematicBreak(@NonNull SpannableTheme theme) {
+        return new ThematicBreakSpan(theme);
+    }
+
+    @Nullable
+    @Override
+    public Object heading(@NonNull SpannableTheme theme, int level) {
+        return new HeadingSpan(theme, level);
+    }
+
+    @Nullable
+    @Override
+    public Object strikethrough() {
+        return new StrikethroughSpan();
+    }
+
+    @Nullable
+    @Override
+    public Object taskListItem(@NonNull SpannableTheme theme, int blockIndent, boolean isDone) {
+        return new TaskListSpan(theme, blockIndent, isDone);
+    }
+
+    @Nullable
+    @Override
+    public Object tableRow(@NonNull SpannableTheme theme, @NonNull List<TableRowSpan.Cell> cells, boolean isHeader, boolean isOdd) {
+        return new TableRowSpan(theme, cells, isHeader, isOdd);
+    }
+
+    @Nullable
+    @Override
+    public Object image(@NonNull SpannableTheme theme, @NonNull String destination, @NonNull AsyncDrawable.Loader loader, @NonNull ImageSizeResolver imageSizeResolver, @Nullable ImageSize imageSize, boolean replacementTextIsLink) {
+        return new AsyncDrawableSpan(
+                theme,
+                new AsyncDrawable(
+                        destination,
+                        loader,
+                        imageSizeResolver,
+                        imageSize
+                ),
+                AsyncDrawableSpan.ALIGN_BOTTOM,
+                replacementTextIsLink
+        );
+    }
+
+    @Nullable
+    @Override
+    public Object link(@NonNull SpannableTheme theme, @NonNull String destination, @NonNull LinkSpan.Resolver resolver) {
+        return new LinkSpan(theme, destination, resolver);
+    }
+}
diff --git a/library/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java b/library/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java
index ceb22ef0..3898f5d0 100644
--- a/library/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java
+++ b/library/src/main/java/ru/noties/markwon/renderer/SpannableMarkdownVisitor.java
@@ -42,6 +42,7 @@ import java.util.List;
 
 import ru.noties.markwon.SpannableBuilder;
 import ru.noties.markwon.SpannableConfiguration;
+import ru.noties.markwon.SpannableFactory;
 import ru.noties.markwon.renderer.html.SpannableHtmlParser;
 import ru.noties.markwon.spans.AsyncDrawable;
 import ru.noties.markwon.spans.AsyncDrawableSpan;
@@ -52,6 +53,7 @@ import ru.noties.markwon.spans.EmphasisSpan;
 import ru.noties.markwon.spans.HeadingSpan;
 import ru.noties.markwon.spans.LinkSpan;
 import ru.noties.markwon.spans.OrderedListItemSpan;
+import ru.noties.markwon.spans.SpannableTheme;
 import ru.noties.markwon.spans.StrongEmphasisSpan;
 import ru.noties.markwon.spans.TableRowSpan;
 import ru.noties.markwon.spans.TaskListSpan;
@@ -66,6 +68,9 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
     private final SpannableBuilder builder;
     private final Deque<HtmlInlineItem> htmlInlineItems;
 
+    private final SpannableTheme theme;
+    private final SpannableFactory factory;
+
     private int blockQuoteIndent;
     private int listLevel;
 
@@ -80,6 +85,9 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
         this.configuration = configuration;
         this.builder = builder;
         this.htmlInlineItems = new ArrayDeque<>(2);
+
+        this.theme = configuration.theme();
+        this.factory = configuration.factory();
     }
 
     @Override
@@ -91,14 +99,14 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
     public void visit(StrongEmphasis strongEmphasis) {
         final int length = builder.length();
         visitChildren(strongEmphasis);
-        setSpan(length, new StrongEmphasisSpan());
+        setSpan(length, factory.strongEmphasis());
     }
 
     @Override
     public void visit(Emphasis emphasis) {
         final int length = builder.length();
         visitChildren(emphasis);
-        setSpan(length, new EmphasisSpan());
+        setSpan(length, factory.emphasis());
     }
 
     @Override
@@ -115,7 +123,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 
         visitChildren(blockQuote);
 
-        setSpan(length, new BlockQuoteSpan(configuration.theme()));
+        setSpan(length, factory.blockQuote(theme));
 
         blockQuoteIndent -= 1;
 
@@ -136,10 +144,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
         builder.append(code.getLiteral());
         builder.append('\u00a0');
 
-        setSpan(length, new CodeSpan(
-                configuration.theme(),
-                false
-        ));
+        setSpan(length, factory.code(theme, false));
     }
 
     @Override
@@ -174,10 +179,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
         );
         builder.append('\u00a0').append('\n');
 
-        setSpan(length, new CodeSpan(
-                configuration.theme(),
-                true
-        ));
+        setSpan(length, factory.code(theme, true));
 
         newLine();
         builder.append('\n');
@@ -217,11 +219,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 
             visitChildren(listItem);
 
-            // todo| in order to provide real RTL experience there must be a way to provide this string
-            setSpan(length, new OrderedListItemSpan(
-                    configuration.theme(),
-                    String.valueOf(start) + "." + '\u00a0'
-            ));
+            setSpan(length, factory.orderedListItem(theme, start));
 
             // after we have visited the children increment start number
             final OrderedList orderedList = (OrderedList) parent;
@@ -231,10 +229,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 
             visitChildren(listItem);
 
-            setSpan(length, new BulletListItemSpan(
-                    configuration.theme(),
-                    listLevel - 1
-            ));
+            setSpan(length, factory.bulletListItem(theme, listLevel - 1));
         }
 
         blockQuoteIndent -= 1;
@@ -250,7 +245,8 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 
         final int length = builder.length();
         builder.append(' '); // without space it won't render
-        setSpan(length, new ThematicBreakSpan(configuration.theme()));
+
+        setSpan(length, factory.thematicBreak(theme));
 
         newLine();
         builder.append('\n');
@@ -263,7 +259,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 
         final int length = builder.length();
         visitChildren(heading);
-        setSpan(length, new HeadingSpan(configuration.theme(), heading.getLevel()));
+        setSpan(length, factory.heading(theme, heading.getLevel()));
 
         newLine();
 
@@ -305,7 +301,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 
             final int length = builder.length();
             visitChildren(customNode);
-            setSpan(length, new StrikethroughSpan());
+            setSpan(length, factory.strikethrough());
 
         } else if (customNode instanceof TaskListItem) {
 
@@ -319,11 +315,7 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 
             visitChildren(customNode);
 
-            setSpan(length, new TaskListSpan(
-                    configuration.theme(),
-                    blockQuoteIndent,
-                    listItem.done()
-            ));
+            setSpan(length, factory.taskListItem(theme, blockQuoteIndent, listItem.done()));
 
             newLine();
 
@@ -356,12 +348,11 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
                 // trimmed from the final result
                 builder.append('\u00a0');
 
-                final TableRowSpan span = new TableRowSpan(
-                        configuration.theme(),
+                final Object span = factory.tableRow(
+                        theme,
                         pendingTableRow,
                         tableRowIsHeader,
-                        tableRows % 2 == 1
-                );
+                        tableRows % 2 == 1);
 
                 tableRows = tableRowIsHeader
                         ? 0
@@ -434,15 +425,12 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
 
         setSpan(
                 length,
-                new AsyncDrawableSpan(
-                        configuration.theme(),
-                        new AsyncDrawable(
-                                destination,
-                                configuration.asyncDrawableLoader(),
-                                configuration.imageSizeResolver(),
-                                null
-                        ),
-                        AsyncDrawableSpan.ALIGN_BOTTOM,
+                factory.image(
+                        theme,
+                        destination,
+                        configuration.asyncDrawableLoader(),
+                        configuration.imageSizeResolver(),
+                        null,
                         link
                 )
         );
@@ -504,11 +492,22 @@ public class SpannableMarkdownVisitor extends AbstractVisitor {
         final int length = builder.length();
         visitChildren(link);
         final String destination = configuration.urlProcessor().process(link.getDestination());
-        setSpan(length, new LinkSpan(configuration.theme(), destination, configuration.linkResolver()));
+        setSpan(length, factory.link(theme, destination, configuration.linkResolver()));
     }
 
-    private void setSpan(int start, @NonNull Object span) {
-        builder.setSpan(span, start, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+    private void setSpan(int start, @Nullable Object span) {
+        if (span != null) {
+
+            final int length = builder.length();
+
+            if (span.getClass().isArray()) {
+                for (Object o: ((Object[]) span)) {
+                    builder.setSpan(o, start, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                }
+            } else {
+                builder.setSpan(span, start, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+            }
+        }
     }
 
     private void newLine() {