Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Think View Laravel Package

topthink/think-view

Official ThinkPHP view component for rendering templates in MVC applications. Assign variables, load view files, and output content through a simple API, integrating smoothly with ThinkPHP projects and compatible template engines.

View on GitHub
Deep Wiki
Context7

Getting Started

topthink/think-view is not a template engine itself — it is the ThinkPHP view adapter that wires think-template (the engine) into the framework’s View contract. You almost never configure it in isolation; it arrives with topthink/framework and exists to expose $this->fetch() / view() inside controllers.

Minimal setup:

  1. composer require topthink/think-view (usually already pulled in by topthink/framework). Verify topthink/think-template is also present — they version-lock together.
  2. Look at config/view.php first. That single file controls view_suffix, view_dir_name, view_path, cache_path, tpl_cache, tpl_begin / tpl_end, taglib_begin / taglib_end, layout_on, layout_name, and the driver type.
  3. Put your first template at app/view/index/index.html.
// app/controller/Index.php
public function index()
{
    return view('index', ['name' => 'Alice', 'items' => [1,2,3]]);
}
<!-- app/view/index/index.html -->
<h1>Hello, {$name}</h1>
{volist name="items" id="item"}
  <li>{$item}</li>
{/volist}

First use case to validate the driver works end-to-end: a single action, one template, one variable, then a {volist} loop. If that renders, the driver and engine are wired correctly.

Implementation Patterns

Controller-driven rendering. The default workflow is return view('template', $vars) (which returns a Response) or $this->fetch('template', $vars) (which returns a string). Prefer view() in HTTP controllers so the response pipeline, status code, and headers stay intact.

Global variables. Use View::share($key, $value) in a middleware or service provider for things like $siteName, $user, or CSRF tokens, rather than repeating assign() in every action.

View::share('siteName', config('app.name'));

Layouts. Enable layout_on => true in config/view.php, create app/view/layout.html, and put {__CONTENT__} where the page body should inject. Layouts are a blunt global switch — if you need per-controller layout overrides, do it via View::assign('view_layout', ...) patterns or switch to inheritance.

Template inheritance and includes. For anything beyond a handful of pages, {extend name="base"} + {block name="content"} + {include file="partials/header"} is cleaner than the layout system because it is per-template and does not fight global config.

Custom taglibraries. Extend think\template\TagLib, name the class with a TagLib suffix, and register it via taglib_pre_load (auto-loaded per render) or taglib_build_in (globally available). This is the natural extension point for reusable UI components, e.g. an {avatar} tag.

Replacing the driver. Implement think\contract\View (or extend think\View), then set 'type' => \app\common\MyView::class in config/view.php. This is how you swap in Twig, Blade, or a preprocessor without touching controller code.

Rendering outside HTTP. View::fetch('report', $data) returns a string — good for HTML-to-PDF, email bodies, or queue jobs. Note that view() (the helper) returns a Response, so it is less useful in CLI consumers.

Testing. Because fetch() returns a string, you can assert on rendered output without booting a full HTTP kernel — instantiate the view with a test config pointing at a fixture template directory.

Gotchas and Tips

{ } delimiter collisions. The default tpl_begin/tpl_end delimiters are { and }, which clash with Vue, Alpine, Angular, and plain JS object literals. Wrap JS blocks in {literal}...{/literal}, or globally change the delimiters in config/view.php. Changing delimiters is a breaking change for every existing template — do it at project start, not mid-project.

Template cache is on by default in production. With tpl_cache => true, edits to .html files are not picked up until the compiled file in runtime/temp is invalidated (touch the source file or clear runtime/temp). Set tpl_cache => false locally and remember to clear runtime/temp on deploy.

view_suffix must match reality. The driver appends view_suffix (default html) when resolving a template name. If your templates are .tpl, you will get a silent “template not found” unless you set 'view_suffix' => 'tpl'.

No auto-escaping. Think-template outputs variables raw by default. {$user.bio} will happily emit a <script> tag. Escape manually ({$bio|htmlspecialchars}) or enforce a project convention. This is the single most common security foot-gun in this stack.

Layout placeholder is exact. {__CONTENT__} must appear literally in the layout file. A typo, a renamed block, or an include that shadows content will render an empty page with no error — the failure is silent.

Path resolution. {include file="..."} resolves relative to the view root, not the current module or template directory. In multi-app layouts this is a frequent surprise; prefer explicit paths from the view root.

Composer version pinning. think-view constrains think-template; manually bumping think-template can break tag parsing (e.g. taglib_begin being ignored, or {volist} silently unparsed). Let the constraints resolve together, and re-run composer update only with both in the same step.

Compiled cache is keyed on file mtime + config. Changing taglib_begin or tpl_begin in config does not invalidate previously compiled templates in every scenario — clear runtime/temp after any delimiter or taglib change, or you will chase ghosts.

fetch() needs an app instance. In pure CLI scripts (cron, standalone test bootstrap) View::fetch() may fail resolving view_path because the driver leans on app()->getAppPath(). Bootstrap the app or pass an explicit view_path in a scoped config.

Debugging aid. Dump View::engine()->getConfig() to confirm the effective config, and inspect runtime/temp to see the compiled PHP for the exact template — the compiled file reveals the resolved path, cache key, and any tag parse results. When a tag “doesn’t work”, reading the compiled file is faster than reading the docs.

Extension points worth knowing. taglib_pre_load (auto-register taglibs per render), taglib_build_in (global taglibs), view_filter hook/event (post-process the HTML string — useful for HTML minification or asset URL rewriting), and type (swap the entire driver). Anything more invasive (custom Template subclass) is usually a sign you should be changing the engine, not patching the adapter.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource