diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -3905,6 +3905,7 @@
     'PhrictionHistoryController' => 'applications/phriction/controller/PhrictionHistoryController.php',
     'PhrictionInfoConduitAPIMethod' => 'applications/phriction/conduit/PhrictionInfoConduitAPIMethod.php',
     'PhrictionListController' => 'applications/phriction/controller/PhrictionListController.php',
+    'PhrictionMarkupPreviewController' => 'applications/phriction/controller/PhrictionMarkupPreviewController.php',
     'PhrictionMoveController' => 'applications/phriction/controller/PhrictionMoveController.php',
     'PhrictionNewController' => 'applications/phriction/controller/PhrictionNewController.php',
     'PhrictionRemarkupRule' => 'applications/phriction/markup/PhrictionRemarkupRule.php',
@@ -8702,6 +8703,7 @@
     'PhrictionHistoryController' => 'PhrictionController',
     'PhrictionInfoConduitAPIMethod' => 'PhrictionConduitAPIMethod',
     'PhrictionListController' => 'PhrictionController',
+    'PhrictionMarkupPreviewController' => 'PhabricatorController',
     'PhrictionMoveController' => 'PhrictionController',
     'PhrictionNewController' => 'PhrictionController',
     'PhrictionRemarkupRule' => 'PhutilRemarkupRule',
diff --git a/src/applications/phriction/application/PhabricatorPhrictionApplication.php b/src/applications/phriction/application/PhabricatorPhrictionApplication.php
--- a/src/applications/phriction/application/PhabricatorPhrictionApplication.php
+++ b/src/applications/phriction/application/PhabricatorPhrictionApplication.php
@@ -59,7 +59,7 @@
         'new/'                        => 'PhrictionNewController',
         'move/(?P<id>[1-9]\d*)/' => 'PhrictionMoveController',
 
-        'preview/' => 'PhabricatorMarkupPreviewController',
+        'preview/(?P<slug>.+/)' => 'PhrictionMarkupPreviewController',
         'diff/(?P<id>[1-9]\d*)/' => 'PhrictionDiffController',
       ),
     );
diff --git a/src/applications/phriction/controller/PhrictionEditController.php b/src/applications/phriction/controller/PhrictionEditController.php
--- a/src/applications/phriction/controller/PhrictionEditController.php
+++ b/src/applications/phriction/controller/PhrictionEditController.php
@@ -272,7 +272,7 @@
 
     $preview = id(new PHUIRemarkupPreviewPanel())
       ->setHeader($content->getTitle())
-      ->setPreviewURI('/phriction/preview/')
+      ->setPreviewURI('/phriction/preview/'.$document->getSlug())
       ->setControlID('document-textarea')
       ->setPreviewType(PHUIRemarkupPreviewPanel::DOCUMENT);
 
diff --git a/src/applications/phriction/controller/PhrictionMarkupPreviewController.php b/src/applications/phriction/controller/PhrictionMarkupPreviewController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phriction/controller/PhrictionMarkupPreviewController.php
@@ -0,0 +1,28 @@
+<?php
+
+final class PhrictionMarkupPreviewController
+  extends PhabricatorController {
+
+  public function processRequest() {
+    $request = $this->getRequest();
+    $viewer = $request->getUser();
+
+    $text = $request->getStr('text');
+    $slug = $request->getURIData('slug');
+
+    $output = PhabricatorMarkupEngine::renderOneObject(
+      id(new PhabricatorMarkupOneOff())
+        ->setPreserveLinebreaks(true)
+        ->setDisableCache(true)
+        ->setContent($text),
+      'default',
+      $viewer,
+      array(
+        'phriction.isPreview' => true,
+        'phriction.slug' => $slug,
+      ));
+
+    return id(new AphrontAjaxResponse())
+      ->setContent($output);
+  }
+}
diff --git a/src/applications/phriction/markup/PhrictionRemarkupRule.php b/src/applications/phriction/markup/PhrictionRemarkupRule.php
--- a/src/applications/phriction/markup/PhrictionRemarkupRule.php
+++ b/src/applications/phriction/markup/PhrictionRemarkupRule.php
@@ -15,6 +15,39 @@
 
   public function markupDocumentLink(array $matches) {
     $link = trim($matches[1]);
+
+    // Handle relative links.
+    if (substr($link, 0, 2) === './') {
+      $base = null;
+      $context = $this->getEngine()->getConfig('contextObject');
+      if ($context !== null && $context instanceof PhrictionContent) {
+        // Handle content when it's being rendered in document view.
+        $base = $context->getSlug();
+      }
+      if ($context !== null && is_array($context) &&
+          idx($context, 'phriction.isPreview')) {
+        // Handle content when it's a preview for the Phriction editor.
+        $base = idx($context, 'phriction.slug');
+      }
+      if ($base !== null) {
+        $base_parts = explode('/', rtrim($base, '/'));
+        $rel_parts = explode('/', substr(rtrim($link, '/'), 2));
+        foreach ($rel_parts as $part) {
+          if ($part === '.') {
+            // Consume standalone dots in a relative path, and do
+            // nothing with them.
+          } else if ($part === '..') {
+            if (count($base_parts) > 0) {
+              array_pop($base_parts);
+            }
+          } else {
+            array_push($base_parts, $part);
+          }
+        }
+        $link = implode('/', $base_parts).'/';
+      }
+    }
+
     $name = trim(idx($matches, 2, $link));
     if (empty($matches[2])) {
       $name = explode('/', trim($name, '/'));
diff --git a/src/applications/phriction/storage/PhrictionContent.php b/src/applications/phriction/storage/PhrictionContent.php
--- a/src/applications/phriction/storage/PhrictionContent.php
+++ b/src/applications/phriction/storage/PhrictionContent.php
@@ -27,7 +27,8 @@
     return PhabricatorMarkupEngine::renderOneObject(
       $this,
       self::MARKUP_FIELD_BODY,
-      $viewer);
+      $viewer,
+      $this);
   }
 
   protected function getConfiguration() {