I would like to code the below figure (a latent variable model) in tikz. However all my attempts to recreate the figure fail despite reading the tikz manual, see code below.

How can I align the nodes and arrows more to the left?

enter image description here

% latent variable model
\begin{tikzpicture}[transform shape, node distance=2cm,
roundnode/.style={circle, draw=black, very thick, minimum size=7mm},
squarednode/.style={rectangle, draw=black, very thick, minimum size=5mm},
arrow/.style = {semithick,-Stealth}

\node[squarednode](X2)[left=1.2cm of latent, align=center]{X2};
\node[squarednode](X1)[above= 0.5cm of X2, align=center]{X1};
\node[squarednode](X3)[below= 0.5cm of X2, align=center]{X3};

\node[text width=3cm, left=1cm of X1]{e1};
\node[text width=3cm, left=1cm of X2]{e2};
\node[text width=3cm, left=1cm of X3]{e3};

\draw[arrow] (T) -- ["\lambda1"] (X1);
\draw[arrow] (T) -- ["\lambda2"] (X2);
\draw[arrow] (T) -- ["\lambda3"] (X3);

\draw[arrow] (e1) -- (X1);
\draw[arrow] (e2) -- (X2);
\draw[arrow] (e3) -- (X3);

  • 1
    Have you noticed that your code doesn't even work? Because X1 is placed relative to X2, you need to define X2 first. And the anchors are called south east/north east (with space). Oct 23, 2017 at 9:48
  • Hi Torbjørn, I updated the tikz code just now. However, I do not understand what I have to change in order to make it work. How do I define X2?
    – Til Hund
    Oct 23, 2017 at 10:11
  • You have defined X2, what I meant is that you needed to move \node [...] (X2) ... before \node [above=of X2] (X1) .., the way you did in your updated code. And in your updated code, you're referring to nodes you haven't named. The text of a node (what you write in {}) is not the same as the name of a node (what you write in ()). You cannot use the text as a coordinate, hence \draw (T) ... doesn't work, you need to use \draw (latent) ... Similarly, the e1/e2/e3 can't be used as coordinates, because those nodes aren't named. Oct 23, 2017 at 10:42

1 Answer 1


There are a couple of problems with your code.

First of all the syntax for specifying a node is

\node [<settings>] (<name>) at (<coordinate>) {<node text>};

The only thing that is mandatory is {<node text>}, so \node {foo}; is perfectly valid.

If you want to draw a line/arrow to/from a node, it is the <name> that you use as a coordinate, not the <node text>. You have


so in order to draw a line from the node containing the text T, you have to use \draw (latent) .... The same goes for the e1/e2/e3 nodes.

I would also say that it would be better to have all the settings in the same brackets, i.e.

\node[roundnode,align=center] (latent) {T};

Except that align=center doesn't really do anything here, as you haven't set the text width, so I would use just

\node[roundnode] (latent) {T};

Next point is \node[text width=3cm, left=1cm of X1]{e1};. I would remove the text width setting here, you don't want the node to be much wider than the text (e1), do you?

There is also a problem in the lines

\draw[arrow] (T) -- ["\lambda1"] (X1);

It looks like you're trying to use the syntax defined by the quotes library, but that is intended as a shorthand for labels, and labels belong to nodes. So I think you must do

\draw[arrow] (T) -- node["$\lambda_1$"] {} (X1);

Note I also used math mode, because \lambda only works in math mode, and added the number as a subscript.

From your image, you probably want $e_1$ and $X_1$ as well.

For the k nodes, I added a suggestion in the code below. I added the Xk node below X3, but note that I added yshift=0.5cm to the settings of the X2 node, to move it up a bit.

The code below also shows an example of how you can shorten the code by using a \foreach loop.

Complete code.

output of code

% latent variable model
  transform shape, node distance=2cm,
  roundnode/.style={circle, draw=black, very thick, minimum size=7mm},
  squarednode/.style={rectangle, draw=black, very thick, minimum size=5mm},
  arrow/.style = {semithick,-Stealth},
  dotnode/.style={fill,inner sep=0pt,minimum size=2pt,circle} % <- this is new

\node[roundnode] (latent) {$T$};
\node[squarednode, left=1.2cm of latent,yshift=0.5cm] (X2) {$X_2$}; % note addition of yshift
\node[squarednode, above=0.5cm of X2]    (X1) {$X_1$};
\node[squarednode, below=0.5cm of X2]    (X3) {$X_3$};
\node[squarednode, below=0.5cm of X3]    (Xk) {$X_k$};

%\node[left=1cm of X1] (e1) {$e_1$};
%\node[left=1cm of X2] (e2) {$e_2$};
%\node[left=1cm of X3] (e3) {$e_3$};
%\node[left=1cm of Xk] (ek) {$e_k$};
%\draw[arrow] (latent) -- node["$\lambda_1$"inner sep=1pt]{} (X1.east);
%\draw[arrow] (latent) -- node["$\lambda_2$"{inner sep=1pt,yshift=-3pt}]{} (X2.east);
%\draw[arrow] (latent) -- node["$\lambda_3$"{inner sep=1pt,yshift=-2pt}]{} (X3.east);
%\draw[arrow] (latent) -- node["$\lambda_k$"inner sep=1pt]{} (Xk.east);
%\draw[arrow] (e1) -- (X1);
%\draw[arrow] (e2) -- (X2);
%\draw[arrow] (e3) -- (X3);
%\draw[arrow] (ek) -- (Xk);

% the following loop does the same as the commented lines above
\foreach \i/\Yshift in {1/0,2/-3pt,3/-2pt,k/0}
   \node[left=1cm of X\i] (e\i) {$e_\i$};
   \draw[arrow] (latent) -- node["$\lambda_\i$"{inner sep=1pt,yshift=\Yshift}]{} (X\i.east);
   \draw[arrow] (e\i) -- (X\i);

% drawing the 
\path (X3) --

  • 1
    I would have used (X\i.east) and possibly [pos=.6]. Oct 23, 2017 at 15:49
  • 1
    @JohnKormylo Agreed on the .east at least. (That was an omission on my part, thanks.) Oct 23, 2017 at 20:12
  • 1
    I tried some experiments with [pos=.6,above] but was not happy with the results. OTOH, the original has shallower slopes. Oct 23, 2017 at 20:45
  • 1
    @JohnKormylo Probably, if you don't use sloped you need individual adjustments (as in the latest iteration of my answer) to get good results. And yes, the original does have less vertical space between the X-nodes, but I just used the value the OP had used. Oct 23, 2017 at 20:52
  • 1
    @TilHund Do you get that from the exact code in my answer? Oct 24, 2017 at 7:58

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .